Comparar commits
140 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 0e58dd79da | |||
| 83dfbb933b | |||
| 100603fdc0 | |||
| 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 | |||
| 084146756b | |||
| e1f5a49c21 | |||
| 143320341e | |||
| e286bb1b64 | |||
| d433f0e08e | |||
| 107149d601 | |||
| fa9b36f829 | |||
| 6775292d63 | |||
| 6835429a28 | |||
| 22135f9f57 | |||
| 0fd06a225c | |||
| 473dcb0947 | |||
| e306f4611c | |||
| c6f4f44619 | |||
| 0865c63745 | |||
| b81c3a6a67 | |||
| cc56e5639d | |||
| 3ce5b358ae | |||
| e968284618 | |||
| c7723179d8 | |||
| 1872f3f94a | |||
| 3b7887ca35 | |||
| d781e63fab | |||
| d1237cdda3 | |||
| 030e3a5d4a | |||
| 4fa4a6a5ed | |||
| 23fb07240b | |||
| 3912dba33a | |||
| d8c479ab1e | |||
| bec67455c1 | |||
| ba6a37a601 | |||
| 03f5091e73 | |||
| 6b6ff08821 | |||
| 35a0ee4893 | |||
| 3519391119 | |||
| 775a1c9ad8 | |||
| ac95844ebd | |||
| c5b90d9507 | |||
| d631f2e070 | |||
| 5bae2ed5ef | |||
| 5af2a657ca | |||
| 71231026a0 | |||
| dfca67b63a | |||
| 7dc6a3b89f | |||
| f985111b62 | |||
| e8d734b1c2 | |||
| a38864fcb4 | |||
| 47bab51474 | |||
| d0d5b2b4ee | |||
| 36967122db | |||
| 3fd67b9fb6 | |||
| 5bef1aa402 | |||
| de9ea19e7e | |||
| a139d1a279 | |||
| 13e624c38f | |||
| 276985f6c3 | |||
| 49f8143f00 | |||
| bea7241910 | |||
| 61b4da944c | |||
| 6fe1868693 | |||
| 9641c7a1e7 | |||
| 3e59a9b316 | |||
| 16e28567a6 | |||
| 3bef42db6b | |||
| 27d23edacc | |||
| e1a48e3c33 | |||
| 5d13f9290f | |||
| c84140d293 | |||
| 838c072ccc | |||
| ed6a708460 | |||
| 0c9dcdafc2 | |||
| f0dc3b2deb | |||
| 84ede3f01f | |||
| 2daf895e43 | |||
| 3b651b2da9 | |||
| 6fd930908c | |||
| 62125a442d | |||
| 2d54fb2ff9 | |||
| e46fad52bb | |||
| f3cfd2b70b | |||
| 78caa1a712 | |||
| 0884ad6517 | |||
| 37fc4e4332 | |||
| 9cc90159f1 | |||
| 4ceee86c66 | |||
| b9ea7c3414 | |||
| 3033e693be | |||
| a1bc01d3b1 |
+4
-1
@@ -58,6 +58,9 @@ if( UNIX AND NOT APPLE )
|
||||
endif()
|
||||
####
|
||||
|
||||
# Enable Q_ASSERT etc. in all builds
|
||||
add_definitions( -DQT_FORCE_ASSERTS )
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(DefineInstallationPaths)
|
||||
include(GenerateExportHeader)
|
||||
@@ -69,7 +72,7 @@ get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||
# if we cannot get it from git, directly try .tag (packages)
|
||||
# this will work if the tar balls have been properly created
|
||||
# via git-archive.
|
||||
if (${GIT_SHA1} STREQUAL "GITDIR-NOTFOUND")
|
||||
if ("${GIT_SHA1}" STREQUAL "GITDIR-NOTFOUND")
|
||||
file(READ ${CMAKE_SOURCE_DIR}/.tag sha1_candidate)
|
||||
string(REPLACE "\n" "" sha1_candidate ${sha1_candidate})
|
||||
if (NOT ${sha1_candidate} STREQUAL "$Format:%H$")
|
||||
|
||||
+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)
|
||||
|
||||
@@ -40,7 +40,6 @@ QT_PLUGINS = [
|
||||
'imageformats/libqico.dylib',
|
||||
'imageformats/libqjpeg.dylib',
|
||||
'imageformats/libqsvg.dylib',
|
||||
'imageformats/libqmng.dylib',
|
||||
]
|
||||
|
||||
QT_PLUGINS_SEARCH_PATH=[
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
From ea4dcc5931d455e4ee3e958ffa54a9f54ab022c8 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Mon, 5 Jan 2015 10:45:25 +0100
|
||||
Subject: [PATCH 1/3] Fix crash on Mac OS if PAC URL contains non-URL legal
|
||||
chars
|
||||
|
||||
macQueryInternal() was retrieving the PAC URL string as-entered by
|
||||
the user in the 'Proxies' tab of the system network settings dialog
|
||||
and passing it to CFURLCreateWithString().
|
||||
|
||||
CFURLCreateWithString() returns null if the input string contains
|
||||
non-URL legal chars or is empty.
|
||||
|
||||
Change-Id: I9166d0433a62c7b2274b5435a7dea0a16997d10e
|
||||
Patch-By: Robert Knight
|
||||
Task-number: QTBUG-36787
|
||||
Reviewed-by: Peter Hartmann <phartmann@blackberry.com>
|
||||
Reviewed-by: Markus Goetz <markus@woboq.com>
|
||||
---
|
||||
src/network/kernel/qnetworkproxy_mac.cpp | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
index 7d26246..81bce0c 100644
|
||||
--- a/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
@@ -221,7 +221,11 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
|
||||
int enabled;
|
||||
if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) {
|
||||
// PAC is enabled
|
||||
- CFStringRef cfPacLocation = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString);
|
||||
+ // kSCPropNetProxiesProxyAutoConfigURLString returns the URL string
|
||||
+ // as entered in the system proxy configuration dialog
|
||||
+ CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString);
|
||||
+ QCFType<CFStringRef> cfPacLocation = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, pacLocationSetting, NULL, NULL,
|
||||
+ kCFStringEncodingUTF8);
|
||||
|
||||
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
|
||||
QCFType<CFDataRef> pacData;
|
||||
--
|
||||
1.8.3.4 (Apple Git-47)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
From a83e4d1d9dd90d4563ce60f27dfb7802a780e33e Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Mon, 5 Jan 2015 11:42:52 +0100
|
||||
Subject: [PATCH 2/3] Fix possible crash when passing an invalid PAC URL
|
||||
|
||||
This commit checks whether CFURLCreateWithString() succeeded.
|
||||
|
||||
It does not appear to be possible to enter an empty URL directly in the
|
||||
PAC configuration dialog but I can't rule out the possibility
|
||||
that it could find its way into the settings via some other means.
|
||||
|
||||
Change-Id: I6c2053d385503bf0330f5ae9fb1ec36a473d425d
|
||||
Patch-By: Robert Knight
|
||||
Task-number: QTBUG-36787
|
||||
Reviewed-by: Markus Goetz <markus@woboq.com>
|
||||
Reviewed-by: Peter Hartmann <phartmann@blackberry.com>
|
||||
---
|
||||
src/network/kernel/qnetworkproxy_mac.cpp | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
index 81bce0c..6be032e 100644
|
||||
--- a/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
@@ -230,6 +230,10 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
|
||||
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
|
||||
QCFType<CFDataRef> pacData;
|
||||
QCFType<CFURLRef> pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL);
|
||||
+ if (!pacUrl) {
|
||||
+ qWarning("Invalid PAC URL \"%s\"", qPrintable(QCFString::toQString(cfPacLocation)));
|
||||
+ return result;
|
||||
+ }
|
||||
SInt32 errorCode;
|
||||
if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, pacUrl, &pacData, NULL, NULL, &errorCode)) {
|
||||
QString pacLocation = QCFString::toQString(cfPacLocation);
|
||||
--
|
||||
1.8.3.4 (Apple Git-47)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
From 83bd9393e5564ea9168fda90c0f44456633a483a Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Mon, 5 Jan 2015 15:22:57 +0100
|
||||
Subject: [PATCH 3/3] Fix crash if PAC script retrieval returns a null CFData
|
||||
instance
|
||||
|
||||
The documentation for CFURLCreateDataAndPropertiesFromResource()
|
||||
does not make this clear but from looking at the CFNetwork implementation
|
||||
and a user stacktrace it appears that this function can return true
|
||||
but not set the data argument under certain circumstances.
|
||||
|
||||
Change-Id: I48034a640d6f47a51cd5883bbafacad4bcbd0415
|
||||
Task-number: QTBUG-36787
|
||||
Patch-By: Robert Knight
|
||||
Reviewed-by: Markus Goetz <markus@woboq.com>
|
||||
Reviewed-by: Peter Hartmann <phartmann@blackberry.com>
|
||||
---
|
||||
src/network/kernel/qnetworkproxy_mac.cpp | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
index 6be032e..a1ac349 100644
|
||||
--- a/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
@@ -240,7 +240,10 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
|
||||
qWarning("Unable to get the PAC script at \"%s\" (%s)", qPrintable(pacLocation), cfurlErrorDescription(errorCode));
|
||||
return result;
|
||||
}
|
||||
-
|
||||
+ if (!pacData) {
|
||||
+ qWarning("\"%s\" returned an empty PAC script", qPrintable(QCFString::toQString(cfPacLocation)));
|
||||
+ return result;
|
||||
+ }
|
||||
QCFType<CFStringRef> pacScript = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, pacData, kCFStringEncodingISOLatin1);
|
||||
if (!pacScript) {
|
||||
// This should never happen, but the documentation says it may return NULL if there was a problem creating the object.
|
||||
--
|
||||
1.8.3.4 (Apple Git-47)
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
From 22f3d359350fd65e4bbe2e9420fcc4460e8a590a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= <morten.sorvig@digia.com>
|
||||
Date: Tue, 10 Mar 2015 22:37:39 +0100
|
||||
Subject: [PATCH] Cocoa: Fix systray SVG icons.
|
||||
|
||||
Regression caused by f3699510.
|
||||
|
||||
Task-number: QTBUG-44686
|
||||
Change-Id: I546422a67d4da29fac196025b09bddcb45c1b641
|
||||
Reviewed-by: Timur Pocheptsov <Timur.Pocheptsov@digia.com>
|
||||
---
|
||||
src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
index e449fd3..8a35705 100755
|
||||
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
@@ -234,6 +234,10 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||
}
|
||||
}
|
||||
|
||||
+ // Handle SVG icons, which do not return anything for availableSizes().
|
||||
+ if (!selectedSize.isValid())
|
||||
+ selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight), mode);
|
||||
+
|
||||
QPixmap pixmap = icon.pixmap(selectedSize, mode);
|
||||
|
||||
// Draw a low-resolution icon if there is not enough pixels for a retina
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
From ee7fea33383726f0bb72e8082a357820e3ee3675 Mon Sep 17 00:00:00 2001
|
||||
From: Jocelyn Turcotte <jturcotte@woboq.com>
|
||||
Date: Tue, 24 Feb 2015 17:02:02 +0100
|
||||
Subject: [PATCH] OSX Fix disapearing tray icon
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
It would happen together with an error:
|
||||
QPainter::begin: Paint device returned engine == 0
|
||||
and would be caused by the size provided to QIcon::pixmap being empty,
|
||||
itself caused by the availableSizes list being empty for the Selected
|
||||
mode.
|
||||
|
||||
This bug was most often hidden by the fact that the Selected icon mode
|
||||
was not triggered properly since we usually only set menuVisible after
|
||||
calling updateIcon, and most of the time when it did, we would overwrite
|
||||
it right after with a Normal mode icon.
|
||||
|
||||
Fix the issue by disabling the broken feature completely since the
|
||||
default Selected icon is grayed out while tray icons are now usually
|
||||
black (or white when selected). To support the dark menu bar mode on
|
||||
10.10 we'll need to use NSImage's setTemplate anyway and that
|
||||
knowing in advance if we can invert the colors ourselves would also
|
||||
better solve the menuVisible usecase.
|
||||
|
||||
Task-number: QTBUG-42910
|
||||
Change-Id: If9ec9659af28ecceb841bfc2f11721e6029fe891
|
||||
Reviewed-by: Morten Johan Sørvig <morten.sorvig@theqtcompany.com>
|
||||
---
|
||||
src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 17 +++--------------
|
||||
1 file changed, 3 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
index 8a35705..d366a3c 100755
|
||||
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
@@ -102,7 +102,6 @@ QT_USE_NAMESPACE
|
||||
QCocoaSystemTrayIcon *systray;
|
||||
NSStatusItem *item;
|
||||
QCocoaMenu *menu;
|
||||
- bool menuVisible;
|
||||
QIcon icon;
|
||||
QT_MANGLE_NAMESPACE(QNSImageView) *imageCell;
|
||||
}
|
||||
@@ -202,8 +201,6 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||
|
||||
m_sys->item->icon = icon;
|
||||
|
||||
- const bool menuVisible = m_sys->item->menu && m_sys->item->menuVisible;
|
||||
-
|
||||
// The reccomended maximum title bar icon height is 18 points
|
||||
// (device independent pixels). The menu height on past and
|
||||
// current OS X versions is 22 points. Provide some future-proofing
|
||||
@@ -218,9 +215,8 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||
// devicePixelRatio for the "best" screen on the system.
|
||||
qreal devicePixelRatio = qApp->devicePixelRatio();
|
||||
const int maxPixmapHeight = maxImageHeight * devicePixelRatio;
|
||||
- const QIcon::Mode mode = menuVisible ? QIcon::Selected : QIcon::Normal;
|
||||
QSize selectedSize;
|
||||
- Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes(mode))) {
|
||||
+ Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes())) {
|
||||
// Select a pixmap based on the height. We want the largest pixmap
|
||||
// with a height smaller or equal to maxPixmapHeight. The pixmap
|
||||
// may rectangular; assume it has a reasonable size. If there is
|
||||
@@ -236,9 +232,9 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||
|
||||
// Handle SVG icons, which do not return anything for availableSizes().
|
||||
if (!selectedSize.isValid())
|
||||
- selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight), mode);
|
||||
+ selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight));
|
||||
|
||||
- QPixmap pixmap = icon.pixmap(selectedSize, mode);
|
||||
+ QPixmap pixmap = icon.pixmap(selectedSize);
|
||||
|
||||
// Draw a low-resolution icon if there is not enough pixels for a retina
|
||||
// icon. This prevents showing a small icon on retina displays.
|
||||
@@ -385,9 +381,6 @@ QT_END_NAMESPACE
|
||||
Q_UNUSED(notification);
|
||||
down = NO;
|
||||
|
||||
- parent->systray->updateIcon(parent->icon);
|
||||
- parent->menuVisible = false;
|
||||
-
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@@ -397,8 +390,6 @@ QT_END_NAMESPACE
|
||||
int clickCount = [mouseEvent clickCount];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
- parent->systray->updateIcon(parent->icon);
|
||||
-
|
||||
if (clickCount == 2) {
|
||||
[self menuTrackingDone:nil];
|
||||
[parent doubleClickSelector:self];
|
||||
@@ -454,7 +445,6 @@ QT_END_NAMESPACE
|
||||
if (self) {
|
||||
item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
|
||||
menu = 0;
|
||||
- menuVisible = false;
|
||||
systray = sys;
|
||||
imageCell = [[QNSImageView alloc] initWithParent:self];
|
||||
[item setView: imageCell];
|
||||
@@ -498,7 +488,6 @@ QT_END_NAMESPACE
|
||||
selector:@selector(menuTrackingDone:)
|
||||
name:NSMenuDidEndTrackingNotification
|
||||
object:m];
|
||||
- menuVisible = true;
|
||||
[item popUpStatusItemMenu: m];
|
||||
}
|
||||
}
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -1,691 +0,0 @@
|
||||
From cff39fba10ffc10ee4dcfdc66ff6528eb26462d3 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Goetz <markus@woboq.com>
|
||||
Date: Fri, 10 Apr 2015 14:09:53 +0200
|
||||
Subject: [PATCH] QNAM: Fix upload corruptions when server closes connection
|
||||
|
||||
This patch fixes several upload corruptions if the server closes the connection
|
||||
while/before we send data into it. They happen inside multiple places in the HTTP
|
||||
layer and are explained in the comments.
|
||||
Corruptions are:
|
||||
* The upload byte device has an in-flight signal with pending upload data, if
|
||||
it gets reset (because server closes the connection) then the re-send of the
|
||||
request was sometimes taking this stale in-flight pending upload data.
|
||||
* Because some signals were DirectConnection and some were QueuedConnection, there
|
||||
was a chance that a direct signal overtakes a queued signal. The state machine
|
||||
then sent data down the socket which was buffered there (and sent later) although
|
||||
it did not match the current state of the state machine when it was actually sent.
|
||||
* A socket was seen as being able to have requests sent even though it was not
|
||||
encrypted yet. This relates to the previous corruption where data is stored inside
|
||||
the socket's buffer and then sent later.
|
||||
|
||||
The included auto test produces all fixed corruptions, I detected no regressions
|
||||
via the other tests.
|
||||
This code also adds a bit of sanity checking to protect from possible further
|
||||
problems.
|
||||
|
||||
[ChangeLog][QtNetwork] Fix HTTP(s) upload corruption when server closes connection
|
||||
|
||||
Change-Id: I54c883925ec897050941498f139c4b523030432e
|
||||
Reviewed-by: Peter Hartmann <peter-qt@hartmann.tk>
|
||||
---
|
||||
src/corelib/io/qnoncontiguousbytedevice.cpp | 18 +++
|
||||
src/corelib/io/qnoncontiguousbytedevice_p.h | 4 +
|
||||
.../access/qhttpnetworkconnectionchannel.cpp | 35 ++++-
|
||||
.../access/qhttpnetworkconnectionchannel_p.h | 2 +
|
||||
src/network/access/qhttpprotocolhandler.cpp | 7 +
|
||||
src/network/access/qhttpthreaddelegate_p.h | 36 ++++-
|
||||
src/network/access/qnetworkreplyhttpimpl.cpp | 25 ++-
|
||||
src/network/access/qnetworkreplyhttpimpl_p.h | 7 +-
|
||||
.../access/qnetworkreply/tst_qnetworkreply.cpp | 175 ++++++++++++++++++++-
|
||||
9 files changed, 279 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/src/corelib/io/qnoncontiguousbytedevice.cpp b/src/corelib/io/qnoncontiguousbytedevice.cpp
|
||||
index 11510a8..760ca3d 100644
|
||||
--- a/src/corelib/io/qnoncontiguousbytedevice.cpp
|
||||
+++ b/src/corelib/io/qnoncontiguousbytedevice.cpp
|
||||
@@ -236,6 +236,11 @@ qint64 QNonContiguousByteDeviceByteArrayImpl::size()
|
||||
return byteArray->size();
|
||||
}
|
||||
|
||||
+qint64 QNonContiguousByteDeviceByteArrayImpl::pos()
|
||||
+{
|
||||
+ return currentPosition;
|
||||
+}
|
||||
+
|
||||
QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QSharedPointer<QRingBuffer> rb)
|
||||
: QNonContiguousByteDevice(), currentPosition(0)
|
||||
{
|
||||
@@ -273,6 +278,11 @@ bool QNonContiguousByteDeviceRingBufferImpl::atEnd()
|
||||
return currentPosition >= size();
|
||||
}
|
||||
|
||||
+qint64 QNonContiguousByteDeviceRingBufferImpl::pos()
|
||||
+{
|
||||
+ return currentPosition;
|
||||
+}
|
||||
+
|
||||
bool QNonContiguousByteDeviceRingBufferImpl::reset()
|
||||
{
|
||||
if (resetDisabled)
|
||||
@@ -406,6 +416,14 @@ qint64 QNonContiguousByteDeviceIoDeviceImpl::size()
|
||||
return device->size() - initialPosition;
|
||||
}
|
||||
|
||||
+qint64 QNonContiguousByteDeviceIoDeviceImpl::pos()
|
||||
+{
|
||||
+ if (device->isSequential())
|
||||
+ return -1;
|
||||
+
|
||||
+ return device->pos();
|
||||
+}
|
||||
+
|
||||
QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)0)
|
||||
{
|
||||
byteDevice = bd;
|
||||
diff --git a/src/corelib/io/qnoncontiguousbytedevice_p.h b/src/corelib/io/qnoncontiguousbytedevice_p.h
|
||||
index c05ae11..4d7b7b0 100644
|
||||
--- a/src/corelib/io/qnoncontiguousbytedevice_p.h
|
||||
+++ b/src/corelib/io/qnoncontiguousbytedevice_p.h
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
virtual const char* readPointer(qint64 maximumLength, qint64 &len) = 0;
|
||||
virtual bool advanceReadPointer(qint64 amount) = 0;
|
||||
virtual bool atEnd() = 0;
|
||||
+ virtual qint64 pos() { return -1; }
|
||||
virtual bool reset() = 0;
|
||||
void disableReset();
|
||||
bool isResetDisabled() { return resetDisabled; }
|
||||
@@ -106,6 +107,7 @@ public:
|
||||
bool atEnd();
|
||||
bool reset();
|
||||
qint64 size();
|
||||
+ qint64 pos() Q_DECL_OVERRIDE;
|
||||
protected:
|
||||
QByteArray* byteArray;
|
||||
qint64 currentPosition;
|
||||
@@ -121,6 +123,7 @@ public:
|
||||
bool atEnd();
|
||||
bool reset();
|
||||
qint64 size();
|
||||
+ qint64 pos() Q_DECL_OVERRIDE;
|
||||
protected:
|
||||
QSharedPointer<QRingBuffer> ringBuffer;
|
||||
qint64 currentPosition;
|
||||
@@ -138,6 +141,7 @@ public:
|
||||
bool atEnd();
|
||||
bool reset();
|
||||
qint64 size();
|
||||
+ qint64 pos() Q_DECL_OVERRIDE;
|
||||
protected:
|
||||
QIODevice* device;
|
||||
QByteArray* currentReadBuffer;
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 9f63280..49c6793 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -106,15 +106,19 @@ void QHttpNetworkConnectionChannel::init()
|
||||
socket->setProxy(QNetworkProxy::NoProxy);
|
||||
#endif
|
||||
|
||||
+ // We want all signals (except the interactive ones) be connected as QueuedConnection
|
||||
+ // because else we're falling into cases where we recurse back into the socket code
|
||||
+ // and mess up the state. Always going to the event loop (and expecting that when reading/writing)
|
||||
+ // is safer.
|
||||
QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
|
||||
this, SLOT(_q_bytesWritten(qint64)),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
QObject::connect(socket, SIGNAL(connected()),
|
||||
this, SLOT(_q_connected()),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
QObject::connect(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(_q_readyRead()),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
|
||||
// The disconnected() and error() signals may already come
|
||||
// while calling connectToHost().
|
||||
@@ -143,13 +147,13 @@ void QHttpNetworkConnectionChannel::init()
|
||||
// won't be a sslSocket if encrypt is false
|
||||
QObject::connect(sslSocket, SIGNAL(encrypted()),
|
||||
this, SLOT(_q_encrypted()),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
|
||||
this, SLOT(_q_sslErrors(QList<QSslError>)),
|
||||
Qt::DirectConnection);
|
||||
QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
|
||||
this, SLOT(_q_encryptedBytesWritten(qint64)),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
|
||||
if (ignoreAllSslErrors)
|
||||
sslSocket->ignoreSslErrors();
|
||||
@@ -186,8 +190,11 @@ void QHttpNetworkConnectionChannel::close()
|
||||
// pendingEncrypt must only be true in between connected and encrypted states
|
||||
pendingEncrypt = false;
|
||||
|
||||
- if (socket)
|
||||
+ if (socket) {
|
||||
+ // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
|
||||
+ // there is no socket yet.
|
||||
socket->close();
|
||||
+ }
|
||||
}
|
||||
|
||||
|
||||
@@ -353,6 +360,14 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
+
|
||||
+ // This code path for ConnectedState
|
||||
+ if (pendingEncrypt) {
|
||||
+ // Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up
|
||||
+ // and corrupt the things sent to the server.
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -659,6 +674,12 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const
|
||||
void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
|
||||
{
|
||||
Q_UNUSED(bytes);
|
||||
+ if (ssl) {
|
||||
+ // In the SSL case we want to send data from encryptedBytesWritten signal since that one
|
||||
+ // is the one going down to the actual network, not only into some SSL buffer.
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
// bytes have been written to the socket. write even more of them :)
|
||||
if (isSocketWriting())
|
||||
sendRequest();
|
||||
@@ -734,7 +755,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
|
||||
|
||||
// ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
|
||||
//channels[i].reconnectAttempts = 2;
|
||||
- if (pendingEncrypt) {
|
||||
+ if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState
|
||||
#ifndef QT_NO_SSL
|
||||
if (connection->sslContext().isNull()) {
|
||||
// this socket is making the 1st handshake for this connection,
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
index 692c0e6..231fe11 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
@@ -83,6 +83,8 @@ typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
|
||||
class QHttpNetworkConnectionChannel : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
+ // TODO: Refactor this to add an EncryptingState (and remove pendingEncrypt).
|
||||
+ // Also add an Unconnected state so IdleState does not have double meaning.
|
||||
enum ChannelState {
|
||||
IdleState = 0, // ready to send request
|
||||
ConnectingState = 1, // connecting to host
|
||||
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
|
||||
index 28e10f7..3357948 100644
|
||||
--- a/src/network/access/qhttpprotocolhandler.cpp
|
||||
+++ b/src/network/access/qhttpprotocolhandler.cpp
|
||||
@@ -368,6 +368,13 @@ bool QHttpProtocolHandler::sendRequest()
|
||||
// nothing to read currently, break the loop
|
||||
break;
|
||||
} else {
|
||||
+ if (m_channel->written != uploadByteDevice->pos()) {
|
||||
+ // Sanity check. This was useful in tracking down an upload corruption.
|
||||
+ qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << m_channel->written << "but read device is at" << uploadByteDevice->pos();
|
||||
+ Q_ASSERT(m_channel->written == uploadByteDevice->pos());
|
||||
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure);
|
||||
+ return false;
|
||||
+ }
|
||||
qint64 currentWriteSize = m_socket->write(readPointer, currentReadSize);
|
||||
if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
|
||||
// socket broke down
|
||||
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
|
||||
index 1661082..b553409 100644
|
||||
--- a/src/network/access/qhttpthreaddelegate_p.h
|
||||
+++ b/src/network/access/qhttpthreaddelegate_p.h
|
||||
@@ -187,6 +187,7 @@ protected:
|
||||
QByteArray m_dataArray;
|
||||
bool m_atEnd;
|
||||
qint64 m_size;
|
||||
+ qint64 m_pos; // to match calls of haveDataSlot with the expected position
|
||||
public:
|
||||
QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s)
|
||||
: QNonContiguousByteDevice(),
|
||||
@@ -194,7 +195,8 @@ public:
|
||||
m_amount(0),
|
||||
m_data(0),
|
||||
m_atEnd(aE),
|
||||
- m_size(s)
|
||||
+ m_size(s),
|
||||
+ m_pos(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -202,6 +204,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
+ qint64 pos() Q_DECL_OVERRIDE
|
||||
+ {
|
||||
+ return m_pos;
|
||||
+ }
|
||||
+
|
||||
const char* readPointer(qint64 maximumLength, qint64 &len)
|
||||
{
|
||||
if (m_amount > 0) {
|
||||
@@ -229,11 +236,10 @@ public:
|
||||
|
||||
m_amount -= a;
|
||||
m_data += a;
|
||||
+ m_pos += a;
|
||||
|
||||
- // To main thread to inform about our state
|
||||
- emit processedData(a);
|
||||
-
|
||||
- // FIXME possible optimization, already ask user thread for some data
|
||||
+ // To main thread to inform about our state. The m_pos will be sent as a sanity check.
|
||||
+ emit processedData(m_pos, a);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -250,10 +256,21 @@ public:
|
||||
{
|
||||
m_amount = 0;
|
||||
m_data = 0;
|
||||
+ m_dataArray.clear();
|
||||
+
|
||||
+ if (wantDataPending) {
|
||||
+ // had requested the user thread to send some data (only 1 in-flight at any moment)
|
||||
+ wantDataPending = false;
|
||||
+ }
|
||||
|
||||
// Communicate as BlockingQueuedConnection
|
||||
bool b = false;
|
||||
emit resetData(&b);
|
||||
+ if (b) {
|
||||
+ // the reset succeeded, we're at pos 0 again
|
||||
+ m_pos = 0;
|
||||
+ // the HTTP code will anyway abort the request if !b.
|
||||
+ }
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -264,8 +281,13 @@ public:
|
||||
|
||||
public slots:
|
||||
// From user thread:
|
||||
- void haveDataSlot(QByteArray dataArray, bool dataAtEnd, qint64 dataSize)
|
||||
+ void haveDataSlot(qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize)
|
||||
{
|
||||
+ if (pos != m_pos) {
|
||||
+ // Sometimes when re-sending a request in the qhttpnetwork* layer there is a pending haveData from the
|
||||
+ // user thread on the way to us. We need to ignore it since it is the data for the wrong(later) chunk.
|
||||
+ return;
|
||||
+ }
|
||||
wantDataPending = false;
|
||||
|
||||
m_dataArray = dataArray;
|
||||
@@ -285,7 +307,7 @@ signals:
|
||||
|
||||
// to main thread:
|
||||
void wantData(qint64);
|
||||
- void processedData(qint64);
|
||||
+ void processedData(qint64 pos, qint64 amount);
|
||||
void resetData(bool *b);
|
||||
};
|
||||
|
||||
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
index 4ce7303..974a101 100644
|
||||
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
@@ -424,6 +424,7 @@ QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate()
|
||||
, synchronous(false)
|
||||
, state(Idle)
|
||||
, statusCode(0)
|
||||
+ , uploadByteDevicePosition(false)
|
||||
, uploadDeviceChoking(false)
|
||||
, outgoingData(0)
|
||||
, bytesUploaded(-1)
|
||||
@@ -863,9 +864,9 @@ void QNetworkReplyHttpImplPrivate::postRequest()
|
||||
q, SLOT(uploadByteDeviceReadyReadSlot()),
|
||||
Qt::QueuedConnection);
|
||||
|
||||
- // From main thread to user thread:
|
||||
- QObject::connect(q, SIGNAL(haveUploadData(QByteArray,bool,qint64)),
|
||||
- forwardUploadDevice, SLOT(haveDataSlot(QByteArray,bool,qint64)), Qt::QueuedConnection);
|
||||
+ // From user thread to http thread:
|
||||
+ QObject::connect(q, SIGNAL(haveUploadData(qint64,QByteArray,bool,qint64)),
|
||||
+ forwardUploadDevice, SLOT(haveDataSlot(qint64,QByteArray,bool,qint64)), Qt::QueuedConnection);
|
||||
QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),
|
||||
forwardUploadDevice, SIGNAL(readyRead()),
|
||||
Qt::QueuedConnection);
|
||||
@@ -873,8 +874,8 @@ void QNetworkReplyHttpImplPrivate::postRequest()
|
||||
// From http thread to user thread:
|
||||
QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
|
||||
q, SLOT(wantUploadDataSlot(qint64)));
|
||||
- QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)),
|
||||
- q, SLOT(sentUploadDataSlot(qint64)));
|
||||
+ QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64, qint64)),
|
||||
+ q, SLOT(sentUploadDataSlot(qint64,qint64)));
|
||||
QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
|
||||
q, SLOT(resetUploadDataSlot(bool*)),
|
||||
Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!
|
||||
@@ -1268,12 +1269,22 @@ void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfig
|
||||
void QNetworkReplyHttpImplPrivate::resetUploadDataSlot(bool *r)
|
||||
{
|
||||
*r = uploadByteDevice->reset();
|
||||
+ if (*r) {
|
||||
+ // reset our own position which is used for the inter-thread communication
|
||||
+ uploadByteDevicePosition = 0;
|
||||
+ }
|
||||
}
|
||||
|
||||
// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
|
||||
-void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 amount)
|
||||
+void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 pos, qint64 amount)
|
||||
{
|
||||
+ if (uploadByteDevicePosition + amount != pos) {
|
||||
+ // Sanity check, should not happen.
|
||||
+ error(QNetworkReply::UnknownNetworkError, "");
|
||||
+ return;
|
||||
+ }
|
||||
uploadByteDevice->advanceReadPointer(amount);
|
||||
+ uploadByteDevicePosition += amount;
|
||||
}
|
||||
|
||||
// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
|
||||
@@ -1298,7 +1309,7 @@ void QNetworkReplyHttpImplPrivate::wantUploadDataSlot(qint64 maxSize)
|
||||
QByteArray dataArray(data, currentUploadDataLength);
|
||||
|
||||
// Communicate back to HTTP thread
|
||||
- emit q->haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
|
||||
+ emit q->haveUploadData(uploadByteDevicePosition, dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
|
||||
}
|
||||
|
||||
void QNetworkReplyHttpImplPrivate::uploadByteDeviceReadyReadSlot()
|
||||
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
|
||||
index 77d9c5a..1940922 100644
|
||||
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
|
||||
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
|
||||
Q_PRIVATE_SLOT(d_func(), void resetUploadDataSlot(bool *r))
|
||||
Q_PRIVATE_SLOT(d_func(), void wantUploadDataSlot(qint64))
|
||||
- Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64))
|
||||
+ Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64,qint64))
|
||||
Q_PRIVATE_SLOT(d_func(), void uploadByteDeviceReadyReadSlot())
|
||||
Q_PRIVATE_SLOT(d_func(), void emitReplyUploadProgress(qint64, qint64))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_cacheSaveDeviceAboutToClose())
|
||||
@@ -144,7 +144,7 @@ signals:
|
||||
|
||||
void startHttpRequestSynchronously();
|
||||
|
||||
- void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
|
||||
+ void haveUploadData(const qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
|
||||
};
|
||||
|
||||
class QNetworkReplyHttpImplPrivate: public QNetworkReplyPrivate
|
||||
@@ -195,6 +195,7 @@ public:
|
||||
// upload
|
||||
QNonContiguousByteDevice* createUploadByteDevice();
|
||||
QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
|
||||
+ qint64 uploadByteDevicePosition;
|
||||
bool uploadDeviceChoking; // if we couldn't readPointer() any data at the moment
|
||||
QIODevice *outgoingData;
|
||||
QSharedPointer<QRingBuffer> outgoingDataBuffer;
|
||||
@@ -283,7 +284,7 @@ public:
|
||||
// From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread:
|
||||
void resetUploadDataSlot(bool *r);
|
||||
void wantUploadDataSlot(qint64);
|
||||
- void sentUploadDataSlot(qint64);
|
||||
+ void sentUploadDataSlot(qint64, qint64);
|
||||
|
||||
// From user's QNonContiguousByteDevice
|
||||
void uploadByteDeviceReadyReadSlot();
|
||||
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
index 3ccedf6..d2edf67 100644
|
||||
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
@@ -457,6 +457,10 @@ private Q_SLOTS:
|
||||
|
||||
void putWithRateLimiting();
|
||||
|
||||
+#ifndef QT_NO_SSL
|
||||
+ void putWithServerClosingConnectionImmediately();
|
||||
+#endif
|
||||
+
|
||||
// NOTE: This test must be last!
|
||||
void parentingRepliesToTheApp();
|
||||
private:
|
||||
@@ -4718,18 +4722,22 @@ void tst_QNetworkReply::ioPostToHttpNoBufferFlag()
|
||||
class SslServer : public QTcpServer {
|
||||
Q_OBJECT
|
||||
public:
|
||||
- SslServer() : socket(0) {};
|
||||
+ SslServer() : socket(0), m_ssl(true) {}
|
||||
void incomingConnection(qintptr socketDescriptor) {
|
||||
QSslSocket *serverSocket = new QSslSocket;
|
||||
serverSocket->setParent(this);
|
||||
|
||||
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
|
||||
+ connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
|
||||
+ if (!m_ssl) {
|
||||
+ emit newPlainConnection(serverSocket);
|
||||
+ return;
|
||||
+ }
|
||||
QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath();
|
||||
if (testDataDir.isEmpty())
|
||||
testDataDir = QCoreApplication::applicationDirPath();
|
||||
|
||||
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
|
||||
- connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
|
||||
serverSocket->setProtocol(QSsl::AnyProtocol);
|
||||
connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), serverSocket, SLOT(ignoreSslErrors()));
|
||||
serverSocket->setLocalCertificate(testDataDir + "/certs/server.pem");
|
||||
@@ -4740,11 +4748,12 @@ public:
|
||||
}
|
||||
}
|
||||
signals:
|
||||
- void newEncryptedConnection();
|
||||
+ void newEncryptedConnection(QSslSocket *s);
|
||||
+ void newPlainConnection(QSslSocket *s);
|
||||
public slots:
|
||||
void encryptedSlot() {
|
||||
socket = (QSslSocket*) sender();
|
||||
- emit newEncryptedConnection();
|
||||
+ emit newEncryptedConnection(socket);
|
||||
}
|
||||
void readyReadSlot() {
|
||||
// for the incoming sockets, not the server socket
|
||||
@@ -4753,6 +4762,7 @@ public slots:
|
||||
|
||||
public:
|
||||
QSslSocket *socket;
|
||||
+ bool m_ssl;
|
||||
};
|
||||
|
||||
// very similar to ioPostToHttpUploadProgress but for SSL
|
||||
@@ -4780,7 +4790,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress()
|
||||
QNetworkReplyPtr reply(manager.post(request, sourceFile));
|
||||
|
||||
QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
|
||||
- connect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
+ connect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), reply.data(), SLOT(ignoreSslErrors()));
|
||||
|
||||
// get the request started and the incoming socket connected
|
||||
@@ -4788,7 +4798,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress()
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QTcpSocket *incomingSocket = server.socket;
|
||||
QVERIFY(incomingSocket);
|
||||
- disconnect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
+ disconnect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
|
||||
|
||||
incomingSocket->setReadBufferSize(1*1024);
|
||||
@@ -7905,6 +7915,159 @@ void tst_QNetworkReply::putWithRateLimiting()
|
||||
}
|
||||
|
||||
|
||||
+#ifndef QT_NO_SSL
|
||||
+
|
||||
+class PutWithServerClosingConnectionImmediatelyHandler: public QObject
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+public:
|
||||
+ bool m_parsedHeaders;
|
||||
+ QByteArray m_receivedData;
|
||||
+ QByteArray m_expectedData;
|
||||
+ QSslSocket *m_socket;
|
||||
+ PutWithServerClosingConnectionImmediatelyHandler(QSslSocket *s, QByteArray expected) :m_parsedHeaders(false), m_expectedData(expected), m_socket(s)
|
||||
+ {
|
||||
+ m_socket->setParent(this);
|
||||
+ connect(m_socket, SIGNAL(readyRead()), SLOT(readyReadSlot()));
|
||||
+ connect(m_socket, SIGNAL(disconnected()), SLOT(disconnectedSlot()));
|
||||
+ }
|
||||
+signals:
|
||||
+ void correctFileUploadReceived();
|
||||
+ void corruptFileUploadReceived();
|
||||
+
|
||||
+public slots:
|
||||
+ void closeDelayed() {
|
||||
+ m_socket->close();
|
||||
+ }
|
||||
+
|
||||
+ void readyReadSlot()
|
||||
+ {
|
||||
+ QByteArray data = m_socket->readAll();
|
||||
+ m_receivedData += data;
|
||||
+ if (!m_parsedHeaders && m_receivedData.contains("\r\n\r\n")) {
|
||||
+ m_parsedHeaders = true;
|
||||
+ QTimer::singleShot(qrand()%10, this, SLOT(closeDelayed())); // simulate random network latency
|
||||
+ // This server simulates a web server connection closing, e.g. because of Apaches MaxKeepAliveRequests or KeepAliveTimeout
|
||||
+ // In this case QNAM needs to re-send the upload data but it had a bug which then corrupts the upload
|
||||
+ // This test catches that.
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+ void disconnectedSlot()
|
||||
+ {
|
||||
+ if (m_parsedHeaders) {
|
||||
+ //qDebug() << m_receivedData.left(m_receivedData.indexOf("\r\n\r\n"));
|
||||
+ m_receivedData = m_receivedData.mid(m_receivedData.indexOf("\r\n\r\n")+4); // check only actual data
|
||||
+ }
|
||||
+ if (m_receivedData.length() > 0 && !m_expectedData.startsWith(m_receivedData)) {
|
||||
+ // We had received some data but it is corrupt!
|
||||
+ qDebug() << "CORRUPT" << m_receivedData.count();
|
||||
+
|
||||
+ // Use this to track down the pattern of the corruption and conclude the source
|
||||
+// QFile a("/tmp/corrupt");
|
||||
+// a.open(QIODevice::WriteOnly);
|
||||
+// a.write(m_receivedData);
|
||||
+// a.close();
|
||||
+
|
||||
+// QFile b("/tmp/correct");
|
||||
+// b.open(QIODevice::WriteOnly);
|
||||
+// b.write(m_expectedData);
|
||||
+// b.close();
|
||||
+ //exit(1);
|
||||
+ emit corruptFileUploadReceived();
|
||||
+ } else {
|
||||
+ emit correctFileUploadReceived();
|
||||
+ }
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+class PutWithServerClosingConnectionImmediatelyServer: public SslServer
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+public:
|
||||
+ int m_correctUploads;
|
||||
+ int m_corruptUploads;
|
||||
+ int m_repliesFinished;
|
||||
+ int m_expectedReplies;
|
||||
+ QByteArray m_expectedData;
|
||||
+ PutWithServerClosingConnectionImmediatelyServer() : SslServer(), m_correctUploads(0), m_corruptUploads(0), m_repliesFinished(0), m_expectedReplies(0)
|
||||
+ {
|
||||
+ QObject::connect(this, SIGNAL(newEncryptedConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*)));
|
||||
+ QObject::connect(this, SIGNAL(newPlainConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*)));
|
||||
+ }
|
||||
+
|
||||
+public slots:
|
||||
+ void createHandlerForConnection(QSslSocket* s) {
|
||||
+ PutWithServerClosingConnectionImmediatelyHandler *handler = new PutWithServerClosingConnectionImmediatelyHandler(s, m_expectedData);
|
||||
+ handler->setParent(this);
|
||||
+ QObject::connect(handler, SIGNAL(correctFileUploadReceived()), this, SLOT(increaseCorrect()));
|
||||
+ QObject::connect(handler, SIGNAL(corruptFileUploadReceived()), this, SLOT(increaseCorrupt()));
|
||||
+ }
|
||||
+ void increaseCorrect() {
|
||||
+ m_correctUploads++;
|
||||
+ }
|
||||
+ void increaseCorrupt() {
|
||||
+ m_corruptUploads++;
|
||||
+ }
|
||||
+ void replyFinished() {
|
||||
+ m_repliesFinished++;
|
||||
+ if (m_repliesFinished == m_expectedReplies) {
|
||||
+ QTestEventLoop::instance().exitLoop();
|
||||
+ }
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+
|
||||
+
|
||||
+void tst_QNetworkReply::putWithServerClosingConnectionImmediately()
|
||||
+{
|
||||
+ const int numUploads = 40;
|
||||
+ qint64 wantedSize = 512*1024; // 512 kB
|
||||
+ QByteArray sourceFile;
|
||||
+ for (int i = 0; i < wantedSize; ++i) {
|
||||
+ sourceFile += (char)'a' +(i%26);
|
||||
+ }
|
||||
+ bool withSsl = false;
|
||||
+
|
||||
+ for (int s = 0; s <= 1; s++) {
|
||||
+ withSsl = (s == 1);
|
||||
+ // Test also needs to run several times because of 9c2ecf89
|
||||
+ for (int j = 0; j < 20; j++) {
|
||||
+ // emulate a minimal https server
|
||||
+ PutWithServerClosingConnectionImmediatelyServer server;
|
||||
+ server.m_ssl = withSsl;
|
||||
+ server.m_expectedData = sourceFile;
|
||||
+ server.m_expectedReplies = numUploads;
|
||||
+ server.listen(QHostAddress(QHostAddress::LocalHost), 0);
|
||||
+
|
||||
+ for (int i = 0; i < numUploads; i++) {
|
||||
+ // create the request
|
||||
+ QUrl url = QUrl(QString("http%1://127.0.0.1:%2/file=%3").arg(withSsl ? "s" : "").arg(server.serverPort()).arg(i));
|
||||
+ QNetworkRequest request(url);
|
||||
+ QNetworkReply *reply = manager.put(request, sourceFile);
|
||||
+ connect(reply, SIGNAL(sslErrors(QList<QSslError>)), reply, SLOT(ignoreSslErrors()));
|
||||
+ connect(reply, SIGNAL(finished()), &server, SLOT(replyFinished()));
|
||||
+ reply->setParent(&server);
|
||||
+ }
|
||||
+
|
||||
+ // get the request started and the incoming socket connected
|
||||
+ QTestEventLoop::instance().enterLoop(10);
|
||||
+
|
||||
+ //qDebug() << "correct=" << server.m_correctUploads << "corrupt=" << server.m_corruptUploads << "expected=" <<numUploads;
|
||||
+
|
||||
+ // Sanity check because ecause of 9c2ecf89 most replies will error out but we want to make sure at least some of them worked
|
||||
+ QVERIFY(server.m_correctUploads > 5);
|
||||
+ // Because actually important is that we don't get any corruption:
|
||||
+ QCOMPARE(server.m_corruptUploads, 0);
|
||||
+
|
||||
+ server.close();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
|
||||
// NOTE: This test must be last testcase in tst_qnetworkreply!
|
||||
void tst_QNetworkReply::parentingRepliesToTheApp()
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -1,434 +0,0 @@
|
||||
From eae0cb09f1310e755c2aff7c1112f7a6c09d7a53 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Goetz <markus@woboq.com>
|
||||
Date: Fri, 19 Jun 2015 15:35:34 +0200
|
||||
Subject: [PATCH] Network: Fix up previous corruption patch
|
||||
|
||||
This is a fix-up for cff39fba10ffc10ee4dcfdc66ff6528eb26462d3.
|
||||
That patch lead to some internal state issues that lead to the QTBUG-47048
|
||||
or to QNetworkReply objects erroring with "Connection Closed" when
|
||||
the server closed the Keep-Alive connection.
|
||||
|
||||
This patch changes the QNAM socket slot connections to be DirectConnection.
|
||||
We don't close the socket anymore in slots where it is anyway in a closed state
|
||||
afterwards. This prevents event/stack recursions.
|
||||
We also flush QSslSocket/QTcpSocket receive buffers when receiving a disconnect
|
||||
so that the developer always gets the full decrypted data from the buffers.
|
||||
|
||||
[ChangeLog][QtNetwork] Fix HTTP issues with "Unknown Error" and "Connection Closed"
|
||||
[ChangeLog][QtNetwork][Sockets] Read OS/encrypted read buffers when connection
|
||||
closed by server.
|
||||
|
||||
Change-Id: Ib4d6a2d0d988317e3a5356f36e8dbcee4590beed
|
||||
Task-number: QTBUG-47048
|
||||
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
|
||||
Reviewed-by: Richard J. Moore <rich@kde.org>
|
||||
---
|
||||
src/network/access/qhttpnetworkconnection.cpp | 1 -
|
||||
.../access/qhttpnetworkconnectionchannel.cpp | 108 +++++++++++++--------
|
||||
.../access/qhttpnetworkconnectionchannel_p.h | 1 +
|
||||
src/network/access/qhttpnetworkreply.cpp | 2 +-
|
||||
src/network/access/qhttpprotocolhandler.cpp | 1 -
|
||||
src/network/socket/qabstractsocket.cpp | 7 +-
|
||||
src/network/ssl/qsslsocket.cpp | 8 ++
|
||||
src/network/ssl/qsslsocket_openssl.cpp | 7 ++
|
||||
.../access/qnetworkreply/tst_qnetworkreply.cpp | 9 +-
|
||||
9 files changed, 94 insertions(+), 50 deletions(-)
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
|
||||
index 365ce55..543c70e 100644
|
||||
--- a/src/network/access/qhttpnetworkconnection.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnection.cpp
|
||||
@@ -917,7 +917,6 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
|
||||
channels[i].resendCurrent = false;
|
||||
- channels[i].state = QHttpNetworkConnectionChannel::IdleState;
|
||||
|
||||
// if this is not possible, error will be emitted and connection terminated
|
||||
if (!channels[i].resetUploadData())
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 49c6793..e2f6307 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -58,6 +58,11 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
|
||||
|
||||
+// Because in-flight when sending a request, the server might close our connection (because the persistent HTTP
|
||||
+// connection times out)
|
||||
+// We use 3 because we can get a _q_error 3 times depending on the timing:
|
||||
+static const int reconnectAttemptsDefault = 3;
|
||||
+
|
||||
QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
|
||||
: socket(0)
|
||||
, ssl(false)
|
||||
@@ -69,7 +74,7 @@ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
|
||||
, resendCurrent(false)
|
||||
, lastStatus(0)
|
||||
, pendingEncrypt(false)
|
||||
- , reconnectAttempts(2)
|
||||
+ , reconnectAttempts(reconnectAttemptsDefault)
|
||||
, authMethod(QAuthenticatorPrivate::None)
|
||||
, proxyAuthMethod(QAuthenticatorPrivate::None)
|
||||
, authenticationCredentialsSent(false)
|
||||
@@ -106,19 +111,18 @@ void QHttpNetworkConnectionChannel::init()
|
||||
socket->setProxy(QNetworkProxy::NoProxy);
|
||||
#endif
|
||||
|
||||
- // We want all signals (except the interactive ones) be connected as QueuedConnection
|
||||
- // because else we're falling into cases where we recurse back into the socket code
|
||||
- // and mess up the state. Always going to the event loop (and expecting that when reading/writing)
|
||||
- // is safer.
|
||||
+ // After some back and forth in all the last years, this is now a DirectConnection because otherwise
|
||||
+ // the state inside the *Socket classes gets messed up, also in conjunction with the socket notifiers
|
||||
+ // which behave slightly differently on Windows vs Linux
|
||||
QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
|
||||
this, SLOT(_q_bytesWritten(qint64)),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
QObject::connect(socket, SIGNAL(connected()),
|
||||
this, SLOT(_q_connected()),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
QObject::connect(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(_q_readyRead()),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
|
||||
// The disconnected() and error() signals may already come
|
||||
// while calling connectToHost().
|
||||
@@ -129,10 +133,10 @@ void QHttpNetworkConnectionChannel::init()
|
||||
qRegisterMetaType<QAbstractSocket::SocketError>();
|
||||
QObject::connect(socket, SIGNAL(disconnected()),
|
||||
this, SLOT(_q_disconnected()),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
|
||||
this, SLOT(_q_error(QAbstractSocket::SocketError)),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
@@ -147,13 +151,13 @@ void QHttpNetworkConnectionChannel::init()
|
||||
// won't be a sslSocket if encrypt is false
|
||||
QObject::connect(sslSocket, SIGNAL(encrypted()),
|
||||
this, SLOT(_q_encrypted()),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
|
||||
this, SLOT(_q_sslErrors(QList<QSslError>)),
|
||||
Qt::DirectConnection);
|
||||
QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
|
||||
this, SLOT(_q_encryptedBytesWritten(qint64)),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
|
||||
if (ignoreAllSslErrors)
|
||||
sslSocket->ignoreSslErrors();
|
||||
@@ -397,7 +401,7 @@ void QHttpNetworkConnectionChannel::allDone()
|
||||
|
||||
// reset the reconnection attempts after we receive a complete reply.
|
||||
// in case of failures, each channel will attempt two reconnects before emitting error.
|
||||
- reconnectAttempts = 2;
|
||||
+ reconnectAttempts = reconnectAttemptsDefault;
|
||||
|
||||
// now the channel can be seen as free/idle again, all signal emissions for the reply have been done
|
||||
if (state != QHttpNetworkConnectionChannel::ClosingState)
|
||||
@@ -651,6 +655,15 @@ void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
|
||||
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
+void QHttpNetworkConnectionChannel::resendCurrentRequest()
|
||||
+{
|
||||
+ requeueCurrentlyPipelinedRequests();
|
||||
+ if (reply)
|
||||
+ resendCurrent = true;
|
||||
+ if (qobject_cast<QHttpNetworkConnection*>(connection))
|
||||
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
+}
|
||||
+
|
||||
bool QHttpNetworkConnectionChannel::isSocketBusy() const
|
||||
{
|
||||
return (state & QHttpNetworkConnectionChannel::BusyState);
|
||||
@@ -694,8 +707,8 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
|
||||
return;
|
||||
}
|
||||
|
||||
- // read the available data before closing
|
||||
- if (isSocketWaiting() || isSocketReading()) {
|
||||
+ // read the available data before closing (also done in _q_error for other codepaths)
|
||||
+ if ((isSocketWaiting() || isSocketReading()) && socket->bytesAvailable()) {
|
||||
if (reply) {
|
||||
state = QHttpNetworkConnectionChannel::ReadingState;
|
||||
_q_receiveReply();
|
||||
@@ -707,7 +720,8 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
|
||||
state = QHttpNetworkConnectionChannel::IdleState;
|
||||
|
||||
requeueCurrentlyPipelinedRequests();
|
||||
- close();
|
||||
+
|
||||
+ pendingEncrypt = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -789,11 +803,19 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
errorCode = QNetworkReply::ConnectionRefusedError;
|
||||
break;
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
- // try to reconnect/resend before sending an error.
|
||||
- // while "Reading" the _q_disconnected() will handle this.
|
||||
- if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
|
||||
+ // This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
|
||||
+ // Depending on timing it can also come three times in a row (first time when we try to write into a closing QSslSocket).
|
||||
+ // The reconnectAttempts handling catches the cases where we can re-send the request.
|
||||
+ if (!reply && state == QHttpNetworkConnectionChannel::IdleState) {
|
||||
+ // Not actually an error, it is normal for Keep-Alive connections to close after some time if no request
|
||||
+ // is sent on them. No need to error the other replies below. Just bail out here.
|
||||
+ // The _q_disconnected will handle the possibly pipelined replies
|
||||
+ return;
|
||||
+ } else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
|
||||
+ // Try to reconnect/resend before sending an error.
|
||||
+ // While "Reading" the _q_disconnected() will handle this.
|
||||
if (reconnectAttempts-- > 0) {
|
||||
- closeAndResendCurrentRequest();
|
||||
+ resendCurrentRequest();
|
||||
return;
|
||||
} else {
|
||||
errorCode = QNetworkReply::RemoteHostClosedError;
|
||||
@@ -818,24 +840,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
// we can ignore the readbuffersize as the data is already
|
||||
// in memory and we will not receive more data on the socket.
|
||||
reply->setReadBufferSize(0);
|
||||
+ reply->setDownstreamLimited(false);
|
||||
_q_receiveReply();
|
||||
-#ifndef QT_NO_SSL
|
||||
- if (ssl) {
|
||||
- // QT_NO_OPENSSL. The QSslSocket can still have encrypted bytes in the plainsocket.
|
||||
- // So we need to check this if the socket is a QSslSocket. When the socket is flushed
|
||||
- // it will force a decrypt of the encrypted data in the plainsocket.
|
||||
- QSslSocket *sslSocket = static_cast<QSslSocket*>(socket);
|
||||
- qint64 beforeFlush = sslSocket->encryptedBytesAvailable();
|
||||
- while (sslSocket->encryptedBytesAvailable()) {
|
||||
- sslSocket->flush();
|
||||
- _q_receiveReply();
|
||||
- qint64 afterFlush = sslSocket->encryptedBytesAvailable();
|
||||
- if (afterFlush == beforeFlush)
|
||||
- break;
|
||||
- beforeFlush = afterFlush;
|
||||
- }
|
||||
+ if (!reply) {
|
||||
+ // No more reply assigned after the previous call? Then it had been finished successfully.
|
||||
+ requeueCurrentlyPipelinedRequests();
|
||||
+ state = QHttpNetworkConnectionChannel::IdleState;
|
||||
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
+ return;
|
||||
}
|
||||
-#endif
|
||||
}
|
||||
|
||||
errorCode = QNetworkReply::RemoteHostClosedError;
|
||||
@@ -846,7 +859,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
case QAbstractSocket::SocketTimeoutError:
|
||||
// try to reconnect/resend before sending an error.
|
||||
if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
|
||||
- closeAndResendCurrentRequest();
|
||||
+ resendCurrentRequest();
|
||||
return;
|
||||
}
|
||||
errorCode = QNetworkReply::TimeoutError;
|
||||
@@ -860,7 +873,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
case QAbstractSocket::ProxyConnectionClosedError:
|
||||
// try to reconnect/resend before sending an error.
|
||||
if (reconnectAttempts-- > 0) {
|
||||
- closeAndResendCurrentRequest();
|
||||
+ resendCurrentRequest();
|
||||
return;
|
||||
}
|
||||
errorCode = QNetworkReply::ProxyConnectionClosedError;
|
||||
@@ -868,7 +881,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
case QAbstractSocket::ProxyConnectionTimeoutError:
|
||||
// try to reconnect/resend before sending an error.
|
||||
if (reconnectAttempts-- > 0) {
|
||||
- closeAndResendCurrentRequest();
|
||||
+ resendCurrentRequest();
|
||||
return;
|
||||
}
|
||||
errorCode = QNetworkReply::ProxyTimeoutError;
|
||||
@@ -916,8 +929,18 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
// send the next request
|
||||
QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
|
||||
- if (that) //signal emission triggered event loop
|
||||
- close();
|
||||
+ if (that) {
|
||||
+ //signal emission triggered event loop
|
||||
+ if (!socket)
|
||||
+ state = QHttpNetworkConnectionChannel::IdleState;
|
||||
+ else if (socket->state() == QAbstractSocket::UnconnectedState)
|
||||
+ state = QHttpNetworkConnectionChannel::IdleState;
|
||||
+ else
|
||||
+ state = QHttpNetworkConnectionChannel::ClosingState;
|
||||
+
|
||||
+ // pendingEncrypt must only be true in between connected and encrypted states
|
||||
+ pendingEncrypt = false;
|
||||
+ }
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
@@ -941,7 +964,8 @@ void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetwor
|
||||
|
||||
void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
|
||||
{
|
||||
- sendRequest();
|
||||
+ if (reply)
|
||||
+ sendRequest();
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
index 231fe11..a834b7d 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
@@ -169,6 +169,7 @@ public:
|
||||
|
||||
void handleUnexpectedEOF();
|
||||
void closeAndResendCurrentRequest();
|
||||
+ void resendCurrentRequest();
|
||||
|
||||
bool isSocketBusy() const;
|
||||
bool isSocketWriting() const;
|
||||
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
|
||||
index 55863a3..8b71bd8 100644
|
||||
--- a/src/network/access/qhttpnetworkreply.cpp
|
||||
+++ b/src/network/access/qhttpnetworkreply.cpp
|
||||
@@ -191,7 +191,7 @@ QByteArray QHttpNetworkReply::readAny()
|
||||
return QByteArray();
|
||||
|
||||
// we'll take the last buffer, so schedule another read from http
|
||||
- if (d->downstreamLimited && d->responseData.bufferCount() == 1)
|
||||
+ if (d->downstreamLimited && d->responseData.bufferCount() == 1 && !isFinished())
|
||||
d->connection->d_func()->readMoreLater(this);
|
||||
return d->responseData.read();
|
||||
}
|
||||
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
|
||||
index 3357948..380aaac 100644
|
||||
--- a/src/network/access/qhttpprotocolhandler.cpp
|
||||
+++ b/src/network/access/qhttpprotocolhandler.cpp
|
||||
@@ -250,7 +250,6 @@ bool QHttpProtocolHandler::sendRequest()
|
||||
if (!m_reply) {
|
||||
// heh, how should that happen!
|
||||
qWarning() << "QAbstractProtocolHandler::sendRequest() called without QHttpNetworkReply";
|
||||
- m_channel->state = QHttpNetworkConnectionChannel::IdleState;
|
||||
return false;
|
||||
}
|
||||
|
||||
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
|
||||
index 2666771..0e82d4a 100644
|
||||
--- a/src/network/socket/qabstractsocket.cpp
|
||||
+++ b/src/network/socket/qabstractsocket.cpp
|
||||
@@ -768,6 +768,7 @@ bool QAbstractSocketPrivate::canReadNotification()
|
||||
void QAbstractSocketPrivate::canCloseNotification()
|
||||
{
|
||||
Q_Q(QAbstractSocket);
|
||||
+ // Note that this method is only called on Windows. Other platforms close in the canReadNotification()
|
||||
|
||||
#if defined (QABSTRACTSOCKET_DEBUG)
|
||||
qDebug("QAbstractSocketPrivate::canCloseNotification()");
|
||||
@@ -777,7 +778,11 @@ void QAbstractSocketPrivate::canCloseNotification()
|
||||
if (isBuffered) {
|
||||
// Try to read to the buffer, if the read fail we can close the socket.
|
||||
newBytes = buffer.size();
|
||||
- if (!readFromSocket()) {
|
||||
+ qint64 oldReadBufferMaxSize = readBufferMaxSize;
|
||||
+ readBufferMaxSize = 0; // temporarily disable max read buffer, we want to empty the OS buffer
|
||||
+ bool hadReadFromSocket = readFromSocket();
|
||||
+ readBufferMaxSize = oldReadBufferMaxSize;
|
||||
+ if (!hadReadFromSocket) {
|
||||
q->disconnectFromHost();
|
||||
return;
|
||||
}
|
||||
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
|
||||
index c1fab94..2b9e923 100644
|
||||
--- a/src/network/ssl/qsslsocket.cpp
|
||||
+++ b/src/network/ssl/qsslsocket.cpp
|
||||
@@ -2294,6 +2294,14 @@ void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error)
|
||||
qCDebug(lcSsl) << "\tstate =" << q->state();
|
||||
qCDebug(lcSsl) << "\terrorString =" << q->errorString();
|
||||
#endif
|
||||
+ // this moves encrypted bytes from plain socket into our buffer
|
||||
+ if (plainSocket->bytesAvailable()) {
|
||||
+ qint64 tmpReadBufferMaxSize = readBufferMaxSize;
|
||||
+ readBufferMaxSize = 0; // reset temporarily so the plain sockets completely drained drained
|
||||
+ transmit();
|
||||
+ readBufferMaxSize = tmpReadBufferMaxSize;
|
||||
+ }
|
||||
+
|
||||
q->setSocketError(plainSocket->error());
|
||||
q->setErrorString(plainSocket->errorString());
|
||||
emit q->error(error);
|
||||
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
index ac4336a..94655fe 100644
|
||||
--- a/src/network/ssl/qsslsocket_openssl.cpp
|
||||
+++ b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
@@ -1419,6 +1419,13 @@ void QSslSocketBackendPrivate::disconnected()
|
||||
{
|
||||
if (plainSocket->bytesAvailable() <= 0)
|
||||
destroySslContext();
|
||||
+ else {
|
||||
+ // Move all bytes into the plain buffer
|
||||
+ qint64 tmpReadBufferMaxSize = readBufferMaxSize;
|
||||
+ readBufferMaxSize = 0; // reset temporarily so the plain socket buffer is completely drained
|
||||
+ transmit();
|
||||
+ readBufferMaxSize = tmpReadBufferMaxSize;
|
||||
+ }
|
||||
//if there is still buffered data in the plain socket, don't destroy the ssl context yet.
|
||||
//it will be destroyed when the socket is deleted.
|
||||
}
|
||||
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
index d2edf67..138f528 100644
|
||||
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
@@ -1051,7 +1051,7 @@ protected:
|
||||
// clean up QAbstractSocket's residue:
|
||||
while (client->bytesToWrite() > 0) {
|
||||
qDebug() << "Still having" << client->bytesToWrite() << "bytes to write, doing that now";
|
||||
- if (!client->waitForBytesWritten(2000)) {
|
||||
+ if (!client->waitForBytesWritten(10000)) {
|
||||
qDebug() << "ERROR: FastSender:" << client->error() << "cleaning up residue";
|
||||
return;
|
||||
}
|
||||
@@ -1071,7 +1071,7 @@ protected:
|
||||
measuredSentBytes += writeNextData(client, bytesToWrite);
|
||||
|
||||
while (client->bytesToWrite() > 0) {
|
||||
- if (!client->waitForBytesWritten(2000)) {
|
||||
+ if (!client->waitForBytesWritten(10000)) {
|
||||
qDebug() << "ERROR: FastSender:" << client->error() << "during blocking write";
|
||||
return;
|
||||
}
|
||||
@@ -7946,7 +7946,7 @@ public slots:
|
||||
m_receivedData += data;
|
||||
if (!m_parsedHeaders && m_receivedData.contains("\r\n\r\n")) {
|
||||
m_parsedHeaders = true;
|
||||
- QTimer::singleShot(qrand()%10, this, SLOT(closeDelayed())); // simulate random network latency
|
||||
+ QTimer::singleShot(qrand()%60, this, SLOT(closeDelayed())); // simulate random network latency
|
||||
// This server simulates a web server connection closing, e.g. because of Apaches MaxKeepAliveRequests or KeepAliveTimeout
|
||||
// In this case QNAM needs to re-send the upload data but it had a bug which then corrupts the upload
|
||||
// This test catches that.
|
||||
@@ -8052,11 +8052,12 @@ void tst_QNetworkReply::putWithServerClosingConnectionImmediately()
|
||||
|
||||
// get the request started and the incoming socket connected
|
||||
QTestEventLoop::instance().enterLoop(10);
|
||||
+ QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
//qDebug() << "correct=" << server.m_correctUploads << "corrupt=" << server.m_corruptUploads << "expected=" <<numUploads;
|
||||
|
||||
// Sanity check because ecause of 9c2ecf89 most replies will error out but we want to make sure at least some of them worked
|
||||
- QVERIFY(server.m_correctUploads > 5);
|
||||
+ QVERIFY(server.m_correctUploads > 2);
|
||||
// Because actually important is that we don't get any corruption:
|
||||
QCOMPARE(server.m_corruptUploads, 0);
|
||||
|
||||
--
|
||||
1.9.1
|
||||
@@ -1,64 +0,0 @@
|
||||
From bc32c0ebc0bc00db84ca2f28eb16ab2e5b53a1b6 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Goetz <markus@woboq.com>
|
||||
Date: Fri, 24 Jul 2015 09:53:20 +0200
|
||||
Subject: [PATCH] QNAM: Fix reply deadlocks on server closing connection
|
||||
|
||||
The _q_readyRead can also be called from readMoreLater() because we implemented
|
||||
it so that bandwidth limited reading can be implemented.
|
||||
This can lead to a race condition if the socket is closing at the specific moment
|
||||
and then deadlock the channel: It will stay unusable with a zombie request.
|
||||
The fix in QHttpProtocolaHandler checks if there is actually bytes available to read
|
||||
from the socket and only then continue.
|
||||
|
||||
The fix in the HTTP channel needs to be done to properly finish the reply in
|
||||
cases of a server replying with HTTP/1.0 or "Connection: close".
|
||||
The delayed incovation of _q_receiveReply will properly finish up the reply.
|
||||
|
||||
Change-Id: I19ce2ae595f91d56386cc7406ccacc9935672b6b
|
||||
Reviewed-by: Richard J. Moore <rich@kde.org>
|
||||
---
|
||||
src/network/access/qhttpnetworkconnectionchannel.cpp | 4 ++++
|
||||
src/network/access/qhttpprotocolhandler.cpp | 7 ++++++-
|
||||
2 files changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 7428f9b..257aa13 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -829,11 +829,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
|
||||
if (!reply->d_func()->expectContent()) {
|
||||
// No content expected, this is a valid way to have the connection closed by the server
|
||||
+ // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
|
||||
+ QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
|
||||
// There was no content-length header and it's not chunked encoding,
|
||||
// so this is a valid way to have the connection closed by the server
|
||||
+ // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
|
||||
+ QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
// ok, we got a disconnect even though we did not expect it
|
||||
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
|
||||
index ab2e3da..a208315 100644
|
||||
--- a/src/network/access/qhttpprotocolhandler.cpp
|
||||
+++ b/src/network/access/qhttpprotocolhandler.cpp
|
||||
@@ -237,7 +237,12 @@ void QHttpProtocolHandler::_q_readyRead()
|
||||
}
|
||||
|
||||
if (m_channel->isSocketWaiting() || m_channel->isSocketReading()) {
|
||||
- m_channel->state = QHttpNetworkConnectionChannel::ReadingState;
|
||||
+ if (m_socket->bytesAvailable()) {
|
||||
+ // We might get a spurious call from readMoreLater()
|
||||
+ // call of the QHttpNetworkConnection even while the socket is disconnecting.
|
||||
+ // Therefore check if there is actually bytes available before changing the channel state.
|
||||
+ m_channel->state = QHttpNetworkConnectionChannel::ReadingState;
|
||||
+ }
|
||||
if (m_reply)
|
||||
_q_receiveReply();
|
||||
}
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
From 63cf5d3d26a6f65938c3cdec1734eac9faaaf8cb Mon Sep 17 00:00:00 2001
|
||||
From: Markus Goetz <markus@woboq.com>
|
||||
Date: Tue, 22 Sep 2015 14:26:24 -0400
|
||||
Subject: [PATCH] QNAM: Assign proper channel before sslErrors() emission
|
||||
|
||||
There can be a race condition where another channel connects
|
||||
and gets the sslErrors() from the socket first. Then the
|
||||
QSslConfiguration from the wrong socket (the default
|
||||
channel 0's socket) was used.
|
||||
|
||||
Task-number: QTBUG-18722
|
||||
Change-Id: Ibbfa48c27f181563745daf540fa792a57cc09682
|
||||
Reviewed-by: Richard J. Moore <rich@kde.org>
|
||||
---
|
||||
src/network/access/qhttpnetworkconnectionchannel.cpp | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 257aa13..477cba2 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -1066,6 +1066,8 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
|
||||
connection->d_func()->pauseConnection();
|
||||
if (pendingEncrypt && !reply)
|
||||
connection->d_func()->dequeueRequest(socket);
|
||||
+ if (reply) // a reply was actually dequeued.
|
||||
+ reply->d_func()->connectionChannel = this; // set correct channel like in sendRequest() and queueRequest();
|
||||
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
|
||||
if (reply)
|
||||
emit reply->sslErrors(errors);
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
From 0df5d079290b4c3b13e58e9397fabdc1dfdba96b Mon Sep 17 00:00:00 2001
|
||||
From: Ulf Hermann <ulf.hermann@theqtcompany.com>
|
||||
Date: Fri, 25 Sep 2015 13:23:46 +0200
|
||||
Subject: [PATCH] Don't let closed http sockets pass as valid connections
|
||||
|
||||
A QAbstractSocket can be close()'d at any time, independently of its
|
||||
current connection state. being closed means that we cannot use it to
|
||||
read or write data, but internally it might still have some data to
|
||||
send or receive, for example to an http server. We can even get a
|
||||
connected() signal after close()'ing the socket.
|
||||
|
||||
We need to catch this condition and mark any pending data not yet
|
||||
written to the socket for resending.
|
||||
|
||||
Task-number: QTBUG-48326
|
||||
Change-Id: I6f61c35f2c567f2a138f8cfe9ade7fd1ec039be6
|
||||
Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
|
||||
---
|
||||
.../access/qhttpnetworkconnectionchannel.cpp | 7 ++-
|
||||
.../tst_qhttpnetworkconnection.cpp | 54 ++++++++++++++++++++++
|
||||
2 files changed, 60 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 293909c..b4eda34 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -272,7 +272,12 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
|
||||
QAbstractSocket::SocketState socketState = socket->state();
|
||||
|
||||
// resend this request after we receive the disconnected signal
|
||||
- if (socketState == QAbstractSocket::ClosingState) {
|
||||
+ // If !socket->isOpen() then we have already called close() on the socket, but there was still a
|
||||
+ // pending connectToHost() for which we hadn't seen a connected() signal, yet. The connected()
|
||||
+ // has now arrived (as indicated by socketState != ClosingState), but we cannot send anything on
|
||||
+ // such a socket anymore.
|
||||
+ if (socketState == QAbstractSocket::ClosingState ||
|
||||
+ (socketState != QAbstractSocket::UnconnectedState && !socket->isOpen())) {
|
||||
if (reply)
|
||||
resendCurrent = true;
|
||||
return false;
|
||||
diff --git a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
|
||||
index 5d072af..0d188a8 100644
|
||||
--- a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
|
||||
+++ b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "private/qhttpnetworkconnection_p.h"
|
||||
#include "private/qnoncontiguousbytedevice_p.h"
|
||||
#include <QAuthenticator>
|
||||
+#include <QTcpServer>
|
||||
|
||||
#include "../../../network-settings.h"
|
||||
|
||||
@@ -106,6 +107,8 @@ private Q_SLOTS:
|
||||
|
||||
void getAndThenDeleteObject();
|
||||
void getAndThenDeleteObject_data();
|
||||
+
|
||||
+ void overlappingCloseAndWrite();
|
||||
};
|
||||
|
||||
tst_QHttpNetworkConnection::tst_QHttpNetworkConnection()
|
||||
@@ -1112,6 +1115,57 @@ void tst_QHttpNetworkConnection::getAndThenDeleteObject()
|
||||
}
|
||||
}
|
||||
|
||||
+class TestTcpServer : public QTcpServer
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+public:
|
||||
+ TestTcpServer() : errorCodeReports(0)
|
||||
+ {
|
||||
+ connect(this, &QTcpServer::newConnection, this, &TestTcpServer::onNewConnection);
|
||||
+ QVERIFY(listen(QHostAddress::LocalHost));
|
||||
+ }
|
||||
+
|
||||
+ int errorCodeReports;
|
||||
+
|
||||
+public slots:
|
||||
+ void onNewConnection()
|
||||
+ {
|
||||
+ QTcpSocket *socket = nextPendingConnection();
|
||||
+ if (!socket)
|
||||
+ return;
|
||||
+ // close socket instantly!
|
||||
+ connect(socket, &QTcpSocket::readyRead, socket, &QTcpSocket::close);
|
||||
+ }
|
||||
+
|
||||
+ void onReply(QNetworkReply::NetworkError code)
|
||||
+ {
|
||||
+ QCOMPARE(code, QNetworkReply::RemoteHostClosedError);
|
||||
+ ++errorCodeReports;
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+void tst_QHttpNetworkConnection::overlappingCloseAndWrite()
|
||||
+{
|
||||
+ // server accepts connections, but closes the socket instantly
|
||||
+ TestTcpServer server;
|
||||
+ QNetworkAccessManager accessManager;
|
||||
+
|
||||
+ // ten requests are scheduled. All should result in an RemoteHostClosed...
|
||||
+ QUrl url;
|
||||
+ url.setScheme(QStringLiteral("http"));
|
||||
+ url.setHost(server.serverAddress().toString());
|
||||
+ url.setPort(server.serverPort());
|
||||
+ for (int i = 0; i < 10; ++i) {
|
||||
+ QNetworkRequest request(url);
|
||||
+ QNetworkReply *reply = accessManager.get(request);
|
||||
+ // Not using Qt5 connection syntax here because of overly baroque syntax to discern between
|
||||
+ // different error() methods.
|
||||
+ QObject::connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
+ &server, SLOT(onReply(QNetworkReply::NetworkError)));
|
||||
+ }
|
||||
+
|
||||
+ QTRY_COMPARE(server.errorCodeReports, 10);
|
||||
+}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QHttpNetworkConnection)
|
||||
--
|
||||
1.9.1
|
||||
@@ -1,113 +0,0 @@
|
||||
From c056e63cea1915667997c982f48296ce5acdcc80 Mon Sep 17 00:00:00 2001
|
||||
From: Lorn Potter <lorn.potter@gmail.com>
|
||||
Date: Tue, 2 Jun 2015 13:22:23 +1000
|
||||
Subject: [PATCH] Make sure to report correct NetworkAccessibility
|
||||
|
||||
Task-number: QTBUG-46323
|
||||
Change-Id: Ibdeb3280091a97d785d4314340678a63e88fb219
|
||||
Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
|
||||
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
|
||||
---
|
||||
src/network/access/qnetworkaccessmanager.cpp | 25 +++++++++++++++++--------
|
||||
src/network/access/qnetworkaccessmanager_p.h | 2 ++
|
||||
2 files changed, 19 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
|
||||
index e878feb..84931cb 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager.cpp
|
||||
+++ b/src/network/access/qnetworkaccessmanager.cpp
|
||||
@@ -472,11 +472,11 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
|
||||
// the QNetworkSession's signals
|
||||
connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)),
|
||||
SLOT(_q_onlineStateChanged(bool)));
|
||||
- // we would need all active configurations to check for
|
||||
- // d->networkConfigurationManager.isOnline(), which is asynchronous
|
||||
- // and potentially expensive. We can just check the configuration here
|
||||
- d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active);
|
||||
}
|
||||
+ // we would need all active configurations to check for
|
||||
+ // d->networkConfigurationManager.isOnline(), which is asynchronous
|
||||
+ // and potentially expensive. We can just check the configuration here
|
||||
+ d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -946,6 +946,7 @@ QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const
|
||||
void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
+ d->defaultAccessControl = false;
|
||||
|
||||
if (d->networkAccessible != accessible) {
|
||||
NetworkAccessibility previous = networkAccessible();
|
||||
@@ -964,7 +965,6 @@ void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkA
|
||||
QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const
|
||||
{
|
||||
Q_D(const QNetworkAccessManager);
|
||||
-
|
||||
if (d->networkSessionRequired) {
|
||||
QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession());
|
||||
if (networkSession) {
|
||||
@@ -975,7 +975,13 @@ QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccess
|
||||
return NotAccessible;
|
||||
} else {
|
||||
// Network accessibility is either disabled or unknown.
|
||||
- return (d->networkAccessible == NotAccessible) ? NotAccessible : UnknownAccessibility;
|
||||
+ if (d->defaultAccessControl) {
|
||||
+ if (d->online)
|
||||
+ return d->networkAccessible;
|
||||
+ else
|
||||
+ return NotAccessible;
|
||||
+ }
|
||||
+ return (d->networkAccessible);
|
||||
}
|
||||
} else {
|
||||
if (d->online)
|
||||
@@ -1568,7 +1574,7 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
|
||||
if (!networkSessionStrongRef) {
|
||||
online = false;
|
||||
|
||||
- if (networkAccessible == QNetworkAccessManager::NotAccessible)
|
||||
+ if (networkAccessible == QNetworkAccessManager::NotAccessible || !online)
|
||||
emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible);
|
||||
else
|
||||
emit q->networkAccessibleChanged(QNetworkAccessManager::UnknownAccessibility);
|
||||
@@ -1616,11 +1622,14 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession
|
||||
if (online) {
|
||||
if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) {
|
||||
online = false;
|
||||
- emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible);
|
||||
+ networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
}
|
||||
} else {
|
||||
if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) {
|
||||
online = true;
|
||||
+ if (defaultAccessControl)
|
||||
+ networkAccessible = QNetworkAccessManager::Accessible;
|
||||
emit q->networkAccessibleChanged(networkAccessible);
|
||||
}
|
||||
}
|
||||
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
|
||||
index f513324..c715da0 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager_p.h
|
||||
+++ b/src/network/access/qnetworkaccessmanager_p.h
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
initializeSession(true),
|
||||
#endif
|
||||
cookieJarCreated(false),
|
||||
+ defaultAccessControl(true),
|
||||
authenticationManager(QSharedPointer<QNetworkAccessAuthenticationManager>::create())
|
||||
{ }
|
||||
~QNetworkAccessManagerPrivate();
|
||||
@@ -164,6 +165,7 @@ public:
|
||||
#endif
|
||||
|
||||
bool cookieJarCreated;
|
||||
+ bool defaultAccessControl;
|
||||
|
||||
// The cache with authorization data:
|
||||
QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
From bb281eea179d50a413f4ec1ff172d27ee48d3a41 Mon Sep 17 00:00:00 2001
|
||||
From: Lorn Potter <lorn.potter@gmail.com>
|
||||
Date: Fri, 17 Jul 2015 15:32:23 +1000
|
||||
Subject: [PATCH] Make sure networkAccessibilityChanged is emitted
|
||||
|
||||
Task-number: QTBUG-46323
|
||||
Change-Id: I8297072b62763136f457ca6ae15282d1c22244f4
|
||||
Reviewed-by: Timo Jyrinki <timo.jyrinki@canonical.com>
|
||||
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
|
||||
---
|
||||
src/network/access/qnetworkaccessmanager.cpp | 70 +++++++++++++++-------
|
||||
src/network/access/qnetworkaccessmanager_p.h | 14 ++++-
|
||||
.../tst_qnetworkaccessmanager.cpp | 31 +++++-----
|
||||
3 files changed, 77 insertions(+), 38 deletions(-)
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
|
||||
index 84931cb..f9e9513 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager.cpp
|
||||
+++ b/src/network/access/qnetworkaccessmanager.cpp
|
||||
@@ -278,7 +278,8 @@ static void ensureInitialized()
|
||||
|
||||
\snippet code/src_network_access_qnetworkaccessmanager.cpp 4
|
||||
|
||||
- Network requests can be reenabled again by calling
|
||||
+ Network requests can be re-enabled again, and this property will resume to
|
||||
+ reflect the actual device state by calling
|
||||
|
||||
\snippet code/src_network_access_qnetworkaccessmanager.cpp 5
|
||||
|
||||
@@ -467,16 +468,12 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
|
||||
qRegisterMetaType<QSharedPointer<char> >();
|
||||
|
||||
#ifndef QT_NO_BEARERMANAGEMENT
|
||||
- if (!d->networkSessionRequired) {
|
||||
- // if a session is required, we track online state through
|
||||
- // the QNetworkSession's signals
|
||||
- connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)),
|
||||
- SLOT(_q_onlineStateChanged(bool)));
|
||||
- }
|
||||
- // we would need all active configurations to check for
|
||||
- // d->networkConfigurationManager.isOnline(), which is asynchronous
|
||||
- // and potentially expensive. We can just check the configuration here
|
||||
- d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active);
|
||||
+ // if a session is required, we track online state through
|
||||
+ // the QNetworkSession's signals if a request is already made.
|
||||
+ // we need to track current accessibility state by default
|
||||
+ //
|
||||
+ connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)),
|
||||
+ SLOT(_q_onlineStateChanged(bool)));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -946,7 +943,8 @@ QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const
|
||||
void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
- d->defaultAccessControl = false;
|
||||
+
|
||||
+ d->defaultAccessControl = accessible == NotAccessible ? false : true;
|
||||
|
||||
if (d->networkAccessible != accessible) {
|
||||
NetworkAccessibility previous = networkAccessible();
|
||||
@@ -965,6 +963,10 @@ void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkA
|
||||
QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const
|
||||
{
|
||||
Q_D(const QNetworkAccessManager);
|
||||
+
|
||||
+ if (d->networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined))
|
||||
+ return UnknownAccessibility;
|
||||
+
|
||||
if (d->networkSessionRequired) {
|
||||
QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession());
|
||||
if (networkSession) {
|
||||
@@ -1622,32 +1624,56 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession
|
||||
if (online) {
|
||||
if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) {
|
||||
online = false;
|
||||
- networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
- emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ if (networkAccessible != QNetworkAccessManager::NotAccessible) {
|
||||
+ networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ }
|
||||
}
|
||||
} else {
|
||||
if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) {
|
||||
online = true;
|
||||
if (defaultAccessControl)
|
||||
- networkAccessible = QNetworkAccessManager::Accessible;
|
||||
- emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ if (networkAccessible != QNetworkAccessManager::Accessible) {
|
||||
+ networkAccessible = QNetworkAccessManager::Accessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline)
|
||||
{
|
||||
- // if the user set a config, we only care whether this one is active.
|
||||
+ Q_Q(QNetworkAccessManager);
|
||||
+ // if the user set a config, we only care whether this one is active.
|
||||
// Otherwise, this QNAM is online if there is an online config.
|
||||
if (customNetworkConfiguration) {
|
||||
online = (networkConfiguration.state() & QNetworkConfiguration::Active);
|
||||
} else {
|
||||
- if (isOnline && online != isOnline) {
|
||||
- networkSessionStrongRef.clear();
|
||||
- networkSessionWeakRef.clear();
|
||||
+ if (online != isOnline) {
|
||||
+ if (isOnline) {
|
||||
+ networkSessionStrongRef.clear();
|
||||
+ networkSessionWeakRef.clear();
|
||||
+ }
|
||||
+ online = isOnline;
|
||||
+ }
|
||||
+ }
|
||||
+ if (online) {
|
||||
+ if (defaultAccessControl) {
|
||||
+ if (networkAccessible != QNetworkAccessManager::Accessible) {
|
||||
+ networkAccessible = QNetworkAccessManager::Accessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ }
|
||||
+ }
|
||||
+ } else if (networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined)) {
|
||||
+ if (networkAccessible != QNetworkAccessManager::UnknownAccessibility) {
|
||||
+ networkAccessible = QNetworkAccessManager::UnknownAccessibility;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (networkAccessible != QNetworkAccessManager::NotAccessible) {
|
||||
+ networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
}
|
||||
-
|
||||
- online = isOnline;
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
|
||||
index c715da0..54ae114 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager_p.h
|
||||
+++ b/src/network/access/qnetworkaccessmanager_p.h
|
||||
@@ -78,7 +78,6 @@ public:
|
||||
customNetworkConfiguration(false),
|
||||
networkSessionRequired(networkConfigurationManager.capabilities()
|
||||
& QNetworkConfigurationManager::NetworkSessionRequired),
|
||||
- networkAccessible(QNetworkAccessManager::Accessible),
|
||||
activeReplyCount(0),
|
||||
online(false),
|
||||
initializeSession(true),
|
||||
@@ -86,7 +85,18 @@ public:
|
||||
cookieJarCreated(false),
|
||||
defaultAccessControl(true),
|
||||
authenticationManager(QSharedPointer<QNetworkAccessAuthenticationManager>::create())
|
||||
- { }
|
||||
+ {
|
||||
+#ifndef QT_NO_BEARERMANAGEMENT
|
||||
+ // we would need all active configurations to check for
|
||||
+ // d->networkConfigurationManager.isOnline(), which is asynchronous
|
||||
+ // and potentially expensive. We can just check the configuration here
|
||||
+ online = (networkConfiguration.state().testFlag(QNetworkConfiguration::Active));
|
||||
+ if (online)
|
||||
+ networkAccessible = QNetworkAccessManager::Accessible;
|
||||
+ else
|
||||
+ networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
+#endif
|
||||
+ }
|
||||
~QNetworkAccessManagerPrivate();
|
||||
|
||||
void _q_replyFinished();
|
||||
diff --git a/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp
|
||||
index b4e4b9c..8ecb57d 100644
|
||||
--- a/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp
|
||||
+++ b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp
|
||||
@@ -74,6 +74,10 @@ void tst_QNetworkAccessManager::networkAccessible()
|
||||
// if there is no session, we cannot know in which state we are in
|
||||
QNetworkAccessManager::NetworkAccessibility initialAccessibility =
|
||||
manager.networkAccessible();
|
||||
+
|
||||
+ if (initialAccessibility == QNetworkAccessManager::UnknownAccessibility)
|
||||
+ QSKIP("Unknown accessibility", SkipAll);
|
||||
+
|
||||
QCOMPARE(manager.networkAccessible(), initialAccessibility);
|
||||
|
||||
manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible);
|
||||
@@ -94,29 +98,28 @@ void tst_QNetworkAccessManager::networkAccessible()
|
||||
QCOMPARE(manager.networkAccessible(), initialAccessibility);
|
||||
|
||||
QNetworkConfigurationManager configManager;
|
||||
- bool sessionRequired = (configManager.capabilities()
|
||||
- & QNetworkConfigurationManager::NetworkSessionRequired);
|
||||
QNetworkConfiguration defaultConfig = configManager.defaultConfiguration();
|
||||
if (defaultConfig.isValid()) {
|
||||
manager.setConfiguration(defaultConfig);
|
||||
|
||||
- // the accessibility has not changed if no session is required
|
||||
- if (sessionRequired) {
|
||||
+ QCOMPARE(spy.count(), 0);
|
||||
+
|
||||
+ if (defaultConfig.state().testFlag(QNetworkConfiguration::Active))
|
||||
+ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::Accessible);
|
||||
+ else
|
||||
+ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible);
|
||||
+
|
||||
+ manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible);
|
||||
+
|
||||
+ if (defaultConfig.state().testFlag(QNetworkConfiguration::Active)) {
|
||||
QCOMPARE(spy.count(), 1);
|
||||
- QCOMPARE(spy.takeFirst().at(0).value<QNetworkAccessManager::NetworkAccessibility>(),
|
||||
- QNetworkAccessManager::Accessible);
|
||||
+ QCOMPARE(QNetworkAccessManager::NetworkAccessibility(spy.takeFirst().at(0).toInt()),
|
||||
+ QNetworkAccessManager::NotAccessible);
|
||||
} else {
|
||||
QCOMPARE(spy.count(), 0);
|
||||
}
|
||||
- QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::Accessible);
|
||||
-
|
||||
- manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible);
|
||||
-
|
||||
- QCOMPARE(spy.count(), 1);
|
||||
- QCOMPARE(QNetworkAccessManager::NetworkAccessibility(spy.takeFirst().at(0).toInt()),
|
||||
- QNetworkAccessManager::NotAccessible);
|
||||
- QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible);
|
||||
}
|
||||
+ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible);
|
||||
#endif
|
||||
}
|
||||
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
From e996d68f6130847637ba287518cff1289cfa48e5 Mon Sep 17 00:00:00 2001
|
||||
From: Lorn Potter <lorn.potter@gmail.com>
|
||||
Date: Fri, 6 Nov 2015 14:22:44 +1000
|
||||
Subject: [PATCH] Make UnknownAccessibility not block requests
|
||||
|
||||
This allows requests to proceed without needing bearer plugins.
|
||||
|
||||
Task-number: QTBUG-49267
|
||||
Change-Id: Ie5ce188ddefebd14d666bb5846e8f93ee2925ed1
|
||||
Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
|
||||
---
|
||||
src/network/access/qnetworkaccessmanager.cpp | 3 +--
|
||||
src/network/access/qnetworkaccessmanager_p.h | 2 ++
|
||||
2 files changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
|
||||
index 086140f..0e5870a 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager.cpp
|
||||
+++ b/src/network/access/qnetworkaccessmanager.cpp
|
||||
@@ -976,7 +976,6 @@ QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccess
|
||||
else
|
||||
return NotAccessible;
|
||||
} else {
|
||||
- // Network accessibility is either disabled or unknown.
|
||||
if (d->defaultAccessControl) {
|
||||
if (d->online)
|
||||
return d->networkAccessible;
|
||||
@@ -1161,7 +1160,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
|
||||
#ifndef QT_NO_BEARERMANAGEMENT
|
||||
// Return a disabled network reply if network access is disabled.
|
||||
// Except if the scheme is empty or file://.
|
||||
- if (!d->networkAccessible && !isLocalFile) {
|
||||
+ if (d->networkAccessible == NotAccessible && !isLocalFile) {
|
||||
return new QDisabledNetworkReply(this, req, op);
|
||||
}
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
|
||||
index 54ae114..3fc33b5 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager_p.h
|
||||
+++ b/src/network/access/qnetworkaccessmanager_p.h
|
||||
@@ -93,6 +93,8 @@ public:
|
||||
online = (networkConfiguration.state().testFlag(QNetworkConfiguration::Active));
|
||||
if (online)
|
||||
networkAccessible = QNetworkAccessManager::Accessible;
|
||||
+ else if (networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined))
|
||||
+ networkAccessible = QNetworkAccessManager::UnknownAccessibility;
|
||||
else
|
||||
networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
#endif
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
From ae9d3f4c6c1a732788cd1f24c6a928cee16c3991 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Tue, 27 Jan 2015 16:58:32 +0100
|
||||
Subject: [PATCH] Win32: Re-init system proxy if internet settings change
|
||||
|
||||
Because Proxy Auto Configuration performs DNS lookups,
|
||||
the proxy settings are being cached. For long-running
|
||||
programs this means that once users switch e.g. from or
|
||||
to company networks with a proxy, they instantly will
|
||||
lose connectivity because we cache the old setting.
|
||||
|
||||
To remedy this, we monitor the Registry (locations
|
||||
courtesy of Chromium's platform support) for changes
|
||||
in its settings, and requery for the current proxy in
|
||||
that case.
|
||||
|
||||
Task-number: QTBUG-3470
|
||||
Task-number: QTBUG-29990
|
||||
Change-Id: Id25a51387bcd232c5f879cea0371038986d0e2de
|
||||
Reviewed-by: Oliver Wolff <oliver.wolff@theqtcompany.com>
|
||||
---
|
||||
src/network/kernel/qnetworkproxy_win.cpp | 86 +++++++++++++++++++++++++++++++-
|
||||
1 file changed, 84 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
|
||||
index da2c020..f7741ce 100644
|
||||
--- a/src/network/kernel/qnetworkproxy_win.cpp
|
||||
+++ b/src/network/kernel/qnetworkproxy_win.cpp
|
||||
@@ -345,12 +345,66 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
|
||||
return removeDuplicateProxies(result);
|
||||
}
|
||||
|
||||
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+namespace {
|
||||
+class QRegistryWatcher {
|
||||
+public:
|
||||
+ void addLocation(HKEY hive, const QString& path)
|
||||
+ {
|
||||
+ HKEY openedKey;
|
||||
+ if (RegOpenKeyEx(hive, reinterpret_cast<const wchar_t*>(path.utf16()), 0, KEY_READ, &openedKey) != ERROR_SUCCESS)
|
||||
+ return;
|
||||
+
|
||||
+ const DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
+ REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY;
|
||||
+
|
||||
+ // Watch the registry key for a change of value.
|
||||
+ HANDLE handle = CreateEvent(NULL, true, false, NULL);
|
||||
+ if (RegNotifyChangeKeyValue(openedKey, true, filter, handle, true) != ERROR_SUCCESS) {
|
||||
+ CloseHandle(handle);
|
||||
+ return;
|
||||
+ }
|
||||
+ m_watchEvents.append(handle);
|
||||
+ m_registryHandles.append(openedKey);
|
||||
+ }
|
||||
+
|
||||
+ bool hasChanged() const {
|
||||
+ return !isEmpty() &&
|
||||
+ WaitForMultipleObjects(m_watchEvents.size(), m_watchEvents.data(), false, 0) < WAIT_OBJECT_0 + m_watchEvents.size();
|
||||
+ }
|
||||
+
|
||||
+ bool isEmpty() const {
|
||||
+ return m_watchEvents.isEmpty();
|
||||
+ }
|
||||
+
|
||||
+ void clear() {
|
||||
+ foreach (HANDLE event, m_watchEvents)
|
||||
+ CloseHandle(event);
|
||||
+ foreach (HKEY key, m_registryHandles)
|
||||
+ RegCloseKey(key);
|
||||
+
|
||||
+ m_watchEvents.clear();
|
||||
+ m_registryHandles.clear();
|
||||
+ }
|
||||
+
|
||||
+ ~QRegistryWatcher() {
|
||||
+ clear();
|
||||
+ }
|
||||
+
|
||||
+private:
|
||||
+ QVector<HANDLE> m_watchEvents;
|
||||
+ QVector<HKEY> m_registryHandles;
|
||||
+};
|
||||
+} // namespace
|
||||
+#endif // !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+
|
||||
class QWindowsSystemProxy
|
||||
{
|
||||
public:
|
||||
QWindowsSystemProxy();
|
||||
~QWindowsSystemProxy();
|
||||
void init();
|
||||
+ void reset();
|
||||
|
||||
QMutex mutex;
|
||||
|
||||
@@ -361,7 +415,9 @@ public:
|
||||
QStringList proxyServerList;
|
||||
QStringList proxyBypass;
|
||||
QList<QNetworkProxy> defaultResult;
|
||||
-
|
||||
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+ QRegistryWatcher proxySettingsWatcher;
|
||||
+#endif
|
||||
bool initialized;
|
||||
bool functional;
|
||||
bool isAutoConfig;
|
||||
@@ -381,16 +437,42 @@ QWindowsSystemProxy::~QWindowsSystemProxy()
|
||||
ptrWinHttpCloseHandle(hHttpSession);
|
||||
}
|
||||
|
||||
+void QWindowsSystemProxy::reset()
|
||||
+{
|
||||
+ autoConfigUrl.clear();
|
||||
+ proxyServerList.clear();
|
||||
+ proxyBypass.clear();
|
||||
+ defaultResult.clear();
|
||||
+ defaultResult << QNetworkProxy::NoProxy;
|
||||
+ functional = false;
|
||||
+ isAutoConfig = false;
|
||||
+}
|
||||
+
|
||||
void QWindowsSystemProxy::init()
|
||||
{
|
||||
- if (initialized)
|
||||
+ bool proxySettingsChanged = false;
|
||||
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+ proxySettingsChanged = proxySettingsWatcher.hasChanged();
|
||||
+#endif
|
||||
+
|
||||
+ if (initialized && !proxySettingsChanged)
|
||||
return;
|
||||
initialized = true;
|
||||
|
||||
+ reset();
|
||||
+
|
||||
#ifdef Q_OS_WINCE
|
||||
// Windows CE does not have any of the following API
|
||||
return;
|
||||
#else
|
||||
+
|
||||
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+ proxySettingsWatcher.clear(); // needs reset to trigger a new detection
|
||||
+ proxySettingsWatcher.addLocation(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
|
||||
+ proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
|
||||
+ proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
|
||||
+#endif
|
||||
+
|
||||
// load the winhttp.dll library
|
||||
QSystemLibrary lib(L"winhttp");
|
||||
if (!lib.load())
|
||||
--
|
||||
1.9.1
|
||||
@@ -1,32 +0,0 @@
|
||||
From c1a67e7dc3a6f8876efa32cdbabbfde1c5a37bc6 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Tue, 31 Mar 2015 17:43:44 +0200
|
||||
Subject: [PATCH] Windows: Do not crash if SSL context is gone after root cert
|
||||
lookup
|
||||
|
||||
On Windows, we perform an extra certificate lookup for root CAs that
|
||||
are not in Windows' (minimal) root store. This check can take up to
|
||||
15 seconds. The SSL context can already be gone once we return. Hence
|
||||
we now check for a non-null SSL context on Windows before proceeding.
|
||||
|
||||
Change-Id: I1951569d9b17da33fa604f7c9d8b33255acf200d
|
||||
Reviewed-by: Richard J. Moore <rich@kde.org>
|
||||
---
|
||||
src/network/ssl/qsslsocket_openssl.cpp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
index 0e1a3e5..b132aec 100644
|
||||
--- a/src/network/ssl/qsslsocket_openssl.cpp
|
||||
+++ b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
@@ -1281,7 +1281,7 @@ void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertifi
|
||||
if (plainSocket)
|
||||
plainSocket->resume();
|
||||
paused = false;
|
||||
- if (checkSslErrors())
|
||||
+ if (checkSslErrors() && ssl)
|
||||
continueHandshake();
|
||||
}
|
||||
|
||||
--
|
||||
1.9.1
|
||||
@@ -1,39 +0,0 @@
|
||||
From cf6881c03d9f08c6ace83defe461423bb87f30d8 Mon Sep 17 00:00:00 2001
|
||||
From: =?utf8?q?Tor=20Arne=20Vestb=C3=B8?= <tor.arne.vestbo@theqtcompany.com>
|
||||
Date: Fri, 15 Jan 2016 14:15:51 +0100
|
||||
Subject: [PATCH] OS X: Ensure system tray icon is prepared even when menu bar
|
||||
is hidden
|
||||
|
||||
On OS X 10.11 (El Capitan) the system menu bar can be automatically
|
||||
hidden, in which case the menu bar height is reported to be 0 when
|
||||
using the menuBarHeight API.
|
||||
|
||||
This resulted in failing to prepare an image for the system tray
|
||||
icon item, making the tray item "invisible".
|
||||
|
||||
Instead we now use the [[NSStatusBar systemStatusBar] thickness]
|
||||
API, which returns the correct height regardless of the menu bar
|
||||
being hidden or not.
|
||||
|
||||
Task-number: QTBUG-48960
|
||||
Change-Id: I208fb8df13754964a6f254cadfbff06dd56c6bab
|
||||
---
|
||||
src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
index a3ffb5b..8152c57 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
@@ -198,7 +198,7 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||
// current OS X versions is 22 points. Provide some future-proofing
|
||||
// by deriving the icon height from the menu height.
|
||||
const int padding = 4;
|
||||
- const int menuHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
|
||||
+ const int menuHeight = [[NSStatusBar systemStatusBar] thickness];
|
||||
const int maxImageHeight = menuHeight - padding;
|
||||
|
||||
// Select pixmap based on the device pixel height. Ideally we would use
|
||||
--
|
||||
2.6.2.2.g1b5ffa3
|
||||
|
||||
@@ -1,55 +1,20 @@
|
||||
## Patches used
|
||||
|
||||
There are our patches on top of Qt 5.4.0, which we are currently
|
||||
using for our binary packages on Windows and Mac OS X. Most of them
|
||||
There are our patches on top of Qt 5.6.2, which we are currently
|
||||
using for our binary packages on Windows and macOS. Most of them
|
||||
have been sent upstream and are part of newer Qt releases.
|
||||
|
||||
All changes are designed to up upstream, and all those that are
|
||||
All changes are designed to be upstream, and all those that are
|
||||
special hacks to Qt will bear a NOUPSTREAM in their name
|
||||
|
||||
The git-style numeration is ordered by order of creation, their
|
||||
purpose is outlined in each patches' front matter.
|
||||
You can apply those patches on a git clone using:
|
||||
|
||||
### Part of Qt v5.4.1 and later
|
||||
* 0001-Fix-crash-on-Mac-OS-if-PAC-URL-contains-non-URL-lega.patch
|
||||
* 0002-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch
|
||||
* 0003-Fix-crash-if-PAC-script-retrieval-returns-a-null-CFD.patch
|
||||
|
||||
### Part of Qt v5.4.2 and later
|
||||
* 0004-Cocoa-Fix-systray-SVG-icons.patch
|
||||
* 0005-OSX-Fix-disapearing-tray-icon.patch
|
||||
* 0007-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch
|
||||
* 0018-Windows-Do-not-crash-if-SSL-context-is-gone-after-ro.patch
|
||||
|
||||
### Part of Qt v5.5.0 and later
|
||||
* 0017-Win32-Re-init-system-proxy-if-internet-settings-chan.patch
|
||||
|
||||
### Part of Qt v5.5.1 and later
|
||||
* 0007-X-Network-Fix-up-previous-corruption-patch.patch
|
||||
* 0008-QNAM-Fix-reply-deadlocks-on-server-closing-connectio.patch
|
||||
* 0014-Fix-SNI-for-TlsV1_0OrLater-TlsV1_1OrLater-and-TlsV1_.patch
|
||||
* 0016-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch
|
||||
* 0011-Make-sure-to-report-correct-NetworkAccessibility.patch
|
||||
|
||||
### Part of Qt v5.5.2 (UNRELEASED!)
|
||||
* 0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch
|
||||
* 0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
|
||||
* 0012-Make-sure-networkAccessibilityChanged-is-emitted.patch
|
||||
|
||||
### Part of Qt v5.6 and later
|
||||
* 0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch
|
||||
* 0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
|
||||
* 0011-Make-sure-to-report-correct-NetworkAccessibility.patch
|
||||
* 0012-Make-sure-networkAccessibilityChanged-is-emitted.patch
|
||||
* 0013-Make-UnknownAccessibility-not-block-requests.patch
|
||||
* 0019-Ensure-system-tray-icon-is-prepared-even-when-menu-bar.patch
|
||||
|
||||
### Part of Qt 5.7 and later
|
||||
* 0015-Remove-legacy-platform-code-in-QSslSocket-for-OS-X-1.patch
|
||||
|
||||
### Not submitted upstream to be part of any release:
|
||||
* 0006-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch
|
||||
This is only needed if you intent to harvest debugging symbols
|
||||
for breakpad.
|
||||
```
|
||||
git am <client>/admin/qt/patches/qtbase/*.patch
|
||||
```
|
||||
|
||||
You can update them using:
|
||||
|
||||
```
|
||||
git format-patch -N --no-signature -o <client>/admin/qt/patches/qtbase/ <v5.x.y>
|
||||
```
|
||||
|
||||
+9
-12
@@ -1,30 +1,27 @@
|
||||
From f3cd07c11e0b7327ffc629f48a89c8c457cdba75 Mon Sep 17 00:00:00 2001
|
||||
From 96c34ce85136cbdc16ef83effa8a13137f7ae4c5 Mon Sep 17 00:00:00 2001
|
||||
From: Jocelyn Turcotte <jturcotte@woboq.com>
|
||||
Date: Fri, 6 Mar 2015 16:12:37 +0100
|
||||
Subject: [PATCH] Fix -force-debug-info with macx-clang
|
||||
Subject: [PATCH] [NOUPSTREAM] Fix -force-debug-info with macx-clang
|
||||
|
||||
---
|
||||
mkspecs/common/clang.conf | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/mkspecs/common/clang.conf b/mkspecs/common/clang.conf
|
||||
index 2c29bb8..110d380 100644
|
||||
index e003b94..e9b3291 100644
|
||||
--- a/mkspecs/common/clang.conf
|
||||
+++ b/mkspecs/common/clang.conf
|
||||
@@ -20,11 +20,13 @@ QMAKE_CFLAGS_ISYSTEM = -isystem
|
||||
QMAKE_CFLAGS_PRECOMPILE = -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT}
|
||||
@@ -21,11 +21,13 @@ QMAKE_CFLAGS_PRECOMPILE = -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_
|
||||
QMAKE_CFLAGS_USE_PRECOMPILE = -Xclang -include-pch -Xclang ${QMAKE_PCH_OUTPUT}
|
||||
QMAKE_CFLAGS_LTCG = -flto
|
||||
QMAKE_CFLAGS_DISABLE_LTCG = -fno-lto
|
||||
+QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO = $$QMAKE_CFLAGS_OPTIMIZE -g
|
||||
|
||||
QMAKE_CXXFLAGS_PRECOMPILE = -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT}
|
||||
QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE
|
||||
QMAKE_CXXFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG
|
||||
QMAKE_CXXFLAGS_CXX11 = -std=c++11
|
||||
QMAKE_CXXFLAGS_DISABLE_LTCG = $$QMAKE_CFLAGS_DISABLE_LTCG
|
||||
+QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
|
||||
|
||||
QMAKE_LFLAGS_CXX11 =
|
||||
QMAKE_LFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG
|
||||
--
|
||||
2.2.0
|
||||
|
||||
QMAKE_CXXFLAGS_CXX11 = -std=c++11
|
||||
QMAKE_CXXFLAGS_CXX14 = -std=c++1y
|
||||
QMAKE_CXXFLAGS_CXX1Z = -std=c++1z
|
||||
+17
-22
@@ -1,7 +1,7 @@
|
||||
From 06818f6d1c602aa3c4f9356324866432d2dd0195 Mon Sep 17 00:00:00 2001
|
||||
From e6bccb1f0d8ca59acb1ffdac74a823c06346e7f3 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Mon, 16 Nov 2015 15:02:37 +0100
|
||||
Subject: [PATCH 1/2] Remove legacy platform code in QSslSocket for OS X < 10.5
|
||||
Subject: [PATCH] Remove legacy platform code in QSslSocket for OS X < 10.5
|
||||
|
||||
This avoids manual symbol lookups and makes the code more readable.
|
||||
Mark identical code.
|
||||
@@ -16,20 +16,18 @@ Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
|
||||
Conflicts:
|
||||
src/network/ssl/qsslsocket_openssl.cpp
|
||||
---
|
||||
src/network/ssl/qsslsocket_openssl.cpp | 83 +++++++++++-----------------------
|
||||
src/network/ssl/qsslsocket_openssl.cpp | 81 +++++++++++-----------------------
|
||||
src/network/ssl/qsslsocket_p.h | 6 +--
|
||||
2 files changed, 28 insertions(+), 61 deletions(-)
|
||||
2 files changed, 26 insertions(+), 61 deletions(-)
|
||||
|
||||
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
index 13fc534..7d0fe00 100644
|
||||
index 82644c1..415f147 100644
|
||||
--- a/src/network/ssl/qsslsocket_openssl.cpp
|
||||
+++ b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
@@ -69,14 +69,19 @@
|
||||
#include <QtCore/qvarlengtharray.h>
|
||||
#include <QLibrary> // for loading the security lib for the CA store
|
||||
@@ -76,14 +76,17 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
+#include <string.h>
|
||||
+
|
||||
+#ifdef Q_OS_DARWIN
|
||||
+# include <private/qcore_mac_p.h>
|
||||
+#endif
|
||||
@@ -50,7 +48,7 @@ index 13fc534..7d0fe00 100644
|
||||
PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = 0;
|
||||
PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = 0;
|
||||
PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = 0;
|
||||
@@ -482,23 +487,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
|
||||
@@ -509,23 +512,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
|
||||
|
||||
#ifndef QT_NO_LIBRARY
|
||||
//load symbols needed to receive certificates from system store
|
||||
@@ -59,23 +57,23 @@ index 13fc534..7d0fe00 100644
|
||||
- if (securityLib.load()) {
|
||||
- ptrSecCertificateCopyData = (PtrSecCertificateCopyData) securityLib.resolve("SecCertificateCopyData");
|
||||
- if (!ptrSecCertificateCopyData)
|
||||
- qWarning("could not resolve symbols in security library"); // should never happen
|
||||
- qCWarning(lcSsl, "could not resolve symbols in security library"); // should never happen
|
||||
-
|
||||
- ptrSecTrustSettingsCopyCertificates = (PtrSecTrustSettingsCopyCertificates) securityLib.resolve("SecTrustSettingsCopyCertificates");
|
||||
- if (!ptrSecTrustSettingsCopyCertificates) { // method was introduced in Leopard, use legacy method if it's not there
|
||||
- ptrSecTrustCopyAnchorCertificates = (PtrSecTrustCopyAnchorCertificates) securityLib.resolve("SecTrustCopyAnchorCertificates");
|
||||
- if (!ptrSecTrustCopyAnchorCertificates)
|
||||
- qWarning("could not resolve symbols in security library"); // should never happen
|
||||
- qCWarning(lcSsl, "could not resolve symbols in security library"); // should never happen
|
||||
- }
|
||||
- } else {
|
||||
- qWarning("could not load security library");
|
||||
- qCWarning(lcSsl, "could not load security library");
|
||||
- }
|
||||
-#elif defined(Q_OS_WIN)
|
||||
+#if defined(Q_OS_WIN)
|
||||
HINSTANCE hLib = LoadLibraryW(L"Crypt32");
|
||||
if (hLib) {
|
||||
#if defined(Q_OS_WINCE)
|
||||
@@ -635,40 +624,22 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
@@ -693,40 +680,22 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
timer.start();
|
||||
#endif
|
||||
QList<QSslCertificate> systemCerts;
|
||||
@@ -101,7 +99,7 @@ index 13fc534..7d0fe00 100644
|
||||
- data = ptrSecCertificateCopyData(cfCert);
|
||||
-
|
||||
- if (data == NULL) {
|
||||
- qWarning("error retrieving a CA certificate from the system store");
|
||||
- qCWarning(lcSsl, "error retrieving a CA certificate from the system store");
|
||||
- } else {
|
||||
- QByteArray rawCert = QByteArray::fromRawData((const char *)CFDataGetBytePtr(data), CFDataGetLength(data));
|
||||
- systemCerts.append(QSslCertificate::fromData(rawCert, QSsl::Der));
|
||||
@@ -127,15 +125,15 @@ index 13fc534..7d0fe00 100644
|
||||
- }
|
||||
- else {
|
||||
- // no detailed error handling here
|
||||
- qWarning("could not retrieve system CA certificates");
|
||||
- qCWarning(lcSsl, "could not retrieve system CA certificates");
|
||||
}
|
||||
}
|
||||
#elif defined(Q_OS_WIN)
|
||||
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
|
||||
index 6e7a2c5..c1a6f05 100644
|
||||
index d651971..17cc7b4 100644
|
||||
--- a/src/network/ssl/qsslsocket_p.h
|
||||
+++ b/src/network/ssl/qsslsocket_p.h
|
||||
@@ -145,11 +145,7 @@ public:
|
||||
@@ -151,11 +151,7 @@ public:
|
||||
static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName);
|
||||
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
|
||||
|
||||
@@ -148,6 +146,3 @@ index 6e7a2c5..c1a6f05 100644
|
||||
static PtrCertOpenSystemStoreW ptrCertOpenSystemStoreW;
|
||||
static PtrCertFindCertificateInStore ptrCertFindCertificateInStore;
|
||||
static PtrCertCloseStore ptrCertCloseStore;
|
||||
--
|
||||
1.9.1
|
||||
|
||||
+13
-16
@@ -1,7 +1,7 @@
|
||||
From 6b9366e7748857f14d5b0f92ced70c08ab5235b7 Mon Sep 17 00:00:00 2001
|
||||
From 9d1120db0973ea7741b13a6555b20ae61f6d037e Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <danimo@owncloud.com>
|
||||
Date: Wed, 25 Nov 2015 12:37:27 +0100
|
||||
Subject: [PATCH 2/2] QSslSocket: evaluate CAs in all keychain categories
|
||||
Subject: [PATCH] QSslSocket: evaluate CAs in all keychain categories
|
||||
|
||||
This will make sure that certs in the domainUser (login),
|
||||
and domainAdmin (per machine) keychain are being picked up
|
||||
@@ -15,7 +15,7 @@ it will be accepted.
|
||||
[ChangeLog][Platform Specific Changes] OS X now accepts trusted
|
||||
certificates from the login and system keychains.
|
||||
|
||||
(Backport of fe3a84138e266c425f11353f7d8dc28a588af89e to Qt 5.4)
|
||||
(Backport of fe3a84138e266c425f11353f7d8dc28a588af89e)
|
||||
|
||||
Task-number: QTBUG-32898
|
||||
Change-Id: Ia23083d5af74388eeee31ba07239735cbbe64368
|
||||
@@ -29,10 +29,10 @@ Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
|
||||
create mode 100644 src/network/ssl/qsslsocket_mac_shared.cpp
|
||||
|
||||
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
|
||||
index 8887f47..6347c20 100644
|
||||
index 549906a..7b202b0 100644
|
||||
--- a/src/network/ssl/qsslsocket.cpp
|
||||
+++ b/src/network/ssl/qsslsocket.cpp
|
||||
@@ -1446,6 +1446,10 @@ QList<QSslCertificate> QSslSocket::defaultCaCertificates()
|
||||
@@ -1508,6 +1508,10 @@ QList<QSslCertificate> QSslSocket::defaultCaCertificates()
|
||||
returned by defaultCaCertificates(). You can replace that database
|
||||
with your own with setDefaultCaCertificates().
|
||||
|
||||
@@ -198,10 +198,10 @@ index 0000000..60fea4c
|
||||
+
|
||||
+QT_END_NAMESPACE
|
||||
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
index 7d0fe00..7415e32 100644
|
||||
index 415f147..7a3cb42 100644
|
||||
--- a/src/network/ssl/qsslsocket_openssl.cpp
|
||||
+++ b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
@@ -71,14 +71,6 @@
|
||||
@@ -76,14 +76,6 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -216,15 +216,15 @@ index 7d0fe00..7415e32 100644
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
@@ -616,6 +608,7 @@ void QSslSocketPrivate::resetDefaultCiphers()
|
||||
setDefaultCiphers(defaultCiphers);
|
||||
@@ -672,6 +664,7 @@ void QSslSocketPrivate::resetDefaultEllipticCurves()
|
||||
setDefaultSupportedEllipticCurves(curves);
|
||||
}
|
||||
|
||||
+#ifndef Q_OS_DARWIN // Apple implementation in qsslsocket_mac_shared.cpp
|
||||
QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
{
|
||||
ensureInitialized();
|
||||
@@ -624,25 +617,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
@@ -680,25 +673,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
timer.start();
|
||||
#endif
|
||||
QList<QSslCertificate> systemCerts;
|
||||
@@ -251,7 +251,7 @@ index 7d0fe00..7415e32 100644
|
||||
if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) {
|
||||
HCERTSTORE hSystemStore;
|
||||
#if defined(Q_OS_WINCE)
|
||||
@@ -719,6 +694,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
@@ -775,6 +750,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
|
||||
return systemCerts;
|
||||
}
|
||||
@@ -260,10 +260,10 @@ index 7d0fe00..7415e32 100644
|
||||
void QSslSocketBackendPrivate::startClientEncryption()
|
||||
{
|
||||
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
|
||||
index 384e149..9546f18 100644
|
||||
index 29c47cd..8eb605b 100644
|
||||
--- a/src/network/ssl/ssl.pri
|
||||
+++ b/src/network/ssl/ssl.pri
|
||||
@@ -45,7 +45,9 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
|
||||
@@ -62,7 +62,9 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
|
||||
ssl/qsslsocket_openssl.cpp \
|
||||
ssl/qsslsocket_openssl_symbols.cpp
|
||||
|
||||
@@ -274,6 +274,3 @@ index 384e149..9546f18 100644
|
||||
|
||||
# Add optional SSL libs
|
||||
# Static linking of OpenSSL with msvc:
|
||||
--
|
||||
1.9.1
|
||||
|
||||
+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"
|
||||
|
||||
+8
-13
@@ -114,7 +114,7 @@ void csync_create(CSYNC **csync, const char *local) {
|
||||
*csync = ctx;
|
||||
}
|
||||
|
||||
void csync_init(CSYNC *ctx) {
|
||||
void csync_init(CSYNC *ctx, const char *db_file) {
|
||||
assert(ctx);
|
||||
/* Do not initialize twice */
|
||||
|
||||
@@ -125,6 +125,9 @@ void csync_init(CSYNC *ctx) {
|
||||
|
||||
ctx->remote.type = REMOTE_REPLICA;
|
||||
|
||||
SAFE_FREE(ctx->statedb.file);
|
||||
ctx->statedb.file = c_strdup(db_file);
|
||||
|
||||
c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp);
|
||||
c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp);
|
||||
|
||||
@@ -146,19 +149,11 @@ int csync_update(CSYNC *ctx) {
|
||||
}
|
||||
ctx->status_code = CSYNC_STATUS_OK;
|
||||
|
||||
/* create/load statedb */
|
||||
rc = asprintf(&ctx->statedb.file, "%s/.csync_journal.db",
|
||||
ctx->local.uri);
|
||||
if (rc < 0) {
|
||||
ctx->status_code = CSYNC_STATUS_MEMORY_ERROR;
|
||||
return rc;
|
||||
}
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Journal: %s", ctx->statedb.file);
|
||||
|
||||
if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) {
|
||||
/* Path of database file is set in csync_init */
|
||||
if (csync_statedb_load(ctx, ctx->statedb.file, &ctx->statedb.db) < 0) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->status_code = CSYNC_STATUS_OK;
|
||||
|
||||
@@ -515,7 +510,6 @@ static void _csync_clean_ctx(CSYNC *ctx)
|
||||
c_rbtree_free(ctx->local.tree);
|
||||
c_rbtree_free(ctx->remote.tree);
|
||||
|
||||
SAFE_FREE(ctx->statedb.file);
|
||||
SAFE_FREE(ctx->remote.root_perms);
|
||||
}
|
||||
|
||||
@@ -572,6 +566,7 @@ int csync_destroy(CSYNC *ctx) {
|
||||
|
||||
_csync_clean_ctx(ctx);
|
||||
|
||||
SAFE_FREE(ctx->statedb.file);
|
||||
SAFE_FREE(ctx->local.uri);
|
||||
SAFE_FREE(ctx->error_string);
|
||||
|
||||
|
||||
+1
-6
@@ -44,11 +44,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct csync_client_certs_s {
|
||||
char *certificatePath;
|
||||
char *certificatePasswd;
|
||||
};
|
||||
|
||||
enum csync_status_codes_e {
|
||||
CSYNC_STATUS_OK = 0,
|
||||
|
||||
@@ -326,7 +321,7 @@ void OCSYNC_EXPORT csync_create(CSYNC **csync, const char *local);
|
||||
*
|
||||
* @param ctx The context to initialize.
|
||||
*/
|
||||
void OCSYNC_EXPORT csync_init(CSYNC *ctx);
|
||||
void OCSYNC_EXPORT csync_init(CSYNC *ctx, const char *db_file);
|
||||
|
||||
/**
|
||||
* @brief Update detection
|
||||
|
||||
@@ -230,6 +230,11 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||
}
|
||||
blen = strlen(bname);
|
||||
|
||||
rc = csync_fnmatch("._sync_*.db*", bname, 0);
|
||||
if (rc == 0) {
|
||||
match = CSYNC_FILE_SILENTLY_EXCLUDED;
|
||||
goto out;
|
||||
}
|
||||
rc = csync_fnmatch(".csync_journal.db*", bname, 0);
|
||||
if (rc == 0) {
|
||||
match = CSYNC_FILE_SILENTLY_EXCLUDED;
|
||||
|
||||
@@ -103,9 +103,6 @@ struct csync_s {
|
||||
|
||||
} callbacks;
|
||||
c_strlist_t *excludes;
|
||||
|
||||
// needed for SSL client certificate support
|
||||
struct csync_client_certs_s *clientCerts;
|
||||
|
||||
struct {
|
||||
char *file;
|
||||
|
||||
@@ -704,8 +704,6 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
}
|
||||
|
||||
while ((dirent = csync_vio_readdir(ctx, dh))) {
|
||||
const char *path = NULL;
|
||||
size_t ulen = 0;
|
||||
int flen;
|
||||
int flag;
|
||||
|
||||
@@ -744,35 +742,6 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Create relative path: For local replica, we need to remove the base path. */
|
||||
path = filename;
|
||||
if (ctx->current == LOCAL_REPLICA) {
|
||||
ulen = strlen(ctx->local.uri) + 1;
|
||||
if (((size_t)flen) < ulen) {
|
||||
csync_vio_file_stat_destroy(dirent);
|
||||
dirent = NULL;
|
||||
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
|
||||
goto error;
|
||||
}
|
||||
path += ulen;
|
||||
}
|
||||
|
||||
|
||||
/* skip ".csync_journal.db" and ".csync_journal.db.ctmp" */
|
||||
/* Isn't this done via csync_exclude already? */
|
||||
if (c_streq(path, ".csync_journal.db")
|
||||
|| c_streq(path, ".csync_journal.db.ctmp")
|
||||
|| c_streq(path, ".csync_journal.db.ctmp-journal")
|
||||
|| c_streq(path, ".csync-progressdatabase")
|
||||
|| c_streq(path, ".csync_journal.db-shm")
|
||||
|| c_streq(path, ".csync_journal.db-wal")
|
||||
|| c_streq(path, ".csync_journal.db-journal")) {
|
||||
csync_vio_file_stat_destroy(dirent);
|
||||
dirent = NULL;
|
||||
SAFE_FREE(filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Only for the local replica we have to stat(), for the remote one we have all data already */
|
||||
if (ctx->replica == LOCAL_REPLICA) {
|
||||
res = csync_vio_stat(ctx, filename, dirent);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "csync_private.h"
|
||||
|
||||
static void setup(void **state) {
|
||||
static int setup(void **state) {
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
|
||||
@@ -33,9 +33,11 @@ static void setup(void **state) {
|
||||
csync_create(&csync, "/tmp/check_csync1");
|
||||
|
||||
*state = csync;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_module(void **state) {
|
||||
static int setup_module(void **state) {
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
|
||||
@@ -44,11 +46,13 @@ static void setup_module(void **state) {
|
||||
|
||||
csync_create(&csync, "/tmp/check_csync1");
|
||||
|
||||
csync_init(csync);
|
||||
csync_init(csync, "foo");
|
||||
*state = csync;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void teardown(void **state) {
|
||||
static int teardown(void **state) {
|
||||
CSYNC *csync = *state;
|
||||
int rc;
|
||||
|
||||
@@ -61,6 +65,8 @@ static void teardown(void **state) {
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_csync_commit(void **state)
|
||||
@@ -88,10 +94,10 @@ static void check_csync_commit_dummy(void **state)
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test_setup_teardown(check_csync_commit, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_commit_dummy, setup_module, teardown),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(check_csync_commit, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_commit_dummy, setup_module, teardown),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -50,11 +50,11 @@ static void check_csync_create(void **state)
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test(check_csync_destroy_null),
|
||||
unit_test(check_csync_create),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(check_csync_destroy_null),
|
||||
cmocka_unit_test(check_csync_create),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,15 +29,16 @@
|
||||
|
||||
#define EXCLUDE_LIST_FILE SOURCEDIR"/../sync-exclude.lst"
|
||||
|
||||
static void setup(void **state) {
|
||||
static int setup(void **state) {
|
||||
CSYNC *csync;
|
||||
|
||||
csync_create(&csync, "/tmp/check_csync1");
|
||||
|
||||
*state = csync;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_init(void **state) {
|
||||
static int setup_init(void **state) {
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
|
||||
@@ -59,9 +60,10 @@ static void setup_init(void **state) {
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = csync;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void teardown(void **state) {
|
||||
static int teardown(void **state) {
|
||||
CSYNC *csync = *state;
|
||||
int rc;
|
||||
|
||||
@@ -74,6 +76,8 @@ static void teardown(void **state) {
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_csync_exclude_add(void **state)
|
||||
@@ -143,6 +147,17 @@ static void check_csync_excluded(void **state)
|
||||
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||
rc = csync_excluded_no_ctx(csync->excludes, "subdir/.csync_journal.db", CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||
|
||||
/* also the new form of the database name */
|
||||
rc = csync_excluded_no_ctx(csync->excludes, "._sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||
rc = csync_excluded_no_ctx(csync->excludes, "._sync_5bdd60bdfcfa.db.ctmp", CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||
rc = csync_excluded_no_ctx(csync->excludes, "._sync_5bdd60bdfcfa.db-shm", CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||
rc = csync_excluded_no_ctx(csync->excludes, "subdir/._sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||
|
||||
|
||||
/* pattern ]*.directory - ignore and remove */
|
||||
rc = csync_excluded_no_ctx(csync->excludes, "my.~directory", CSYNC_FTW_TYPE_FILE);
|
||||
@@ -380,16 +395,16 @@ static void check_csync_exclude_expand_escapes(void **state)
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test_setup_teardown(check_csync_exclude_add, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_exclude_load, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_excluded, setup_init, teardown),
|
||||
unit_test_setup_teardown(check_csync_excluded_traversal, setup_init, teardown),
|
||||
unit_test_setup_teardown(check_csync_pathes, setup_init, teardown),
|
||||
unit_test_setup_teardown(check_csync_is_windows_reserved_word, setup_init, teardown),
|
||||
unit_test_setup_teardown(check_csync_excluded_performance, setup_init, teardown),
|
||||
unit_test(check_csync_exclude_expand_escapes),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(check_csync_exclude_add, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_exclude_load, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_excluded, setup_init, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_excluded_traversal, setup_init, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_pathes, setup_init, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_is_windows_reserved_word, setup_init, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_excluded_performance, setup_init, teardown),
|
||||
cmocka_unit_test(check_csync_exclude_expand_escapes),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "csync_private.h"
|
||||
|
||||
static void setup(void **state) {
|
||||
static int setup(void **state) {
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
|
||||
@@ -33,9 +33,10 @@ static void setup(void **state) {
|
||||
csync_create(&csync, "/tmp/check_csync1");
|
||||
|
||||
*state = csync;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_module(void **state) {
|
||||
static int setup_module(void **state) {
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
|
||||
@@ -45,9 +46,10 @@ static void setup_module(void **state) {
|
||||
csync_create(&csync, "/tmp/check_csync1");
|
||||
|
||||
*state = csync;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void teardown(void **state) {
|
||||
static int teardown(void **state) {
|
||||
CSYNC *csync = *state;
|
||||
int rc;
|
||||
|
||||
@@ -60,24 +62,27 @@ static void teardown(void **state) {
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_csync_init(void **state)
|
||||
{
|
||||
CSYNC *csync = *state;
|
||||
|
||||
csync_init(csync);
|
||||
csync_init(csync, "");
|
||||
|
||||
assert_int_equal(csync->status & CSYNC_STATUS_INIT, 1);
|
||||
|
||||
}
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test_setup_teardown(check_csync_init, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_init, setup_module, teardown),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(check_csync_init, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_init, setup_module, teardown),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "csync_log.c"
|
||||
#include "c_private.h"
|
||||
|
||||
static void setup(void **state) {
|
||||
static int setup(void **state) {
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
|
||||
@@ -36,9 +36,11 @@ static void setup(void **state) {
|
||||
csync_create(&csync, "/tmp/check_csync1");
|
||||
|
||||
*state = csync;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void teardown(void **state) {
|
||||
static int teardown(void **state) {
|
||||
CSYNC *csync = *state;
|
||||
int rc;
|
||||
|
||||
@@ -51,6 +53,8 @@ static void teardown(void **state) {
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_log_callback(int verbosity,
|
||||
@@ -134,11 +138,11 @@ static void check_logging(void **state)
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test(check_set_log_level),
|
||||
unit_test(check_set_auth_callback),
|
||||
unit_test_setup_teardown(check_logging, setup, teardown),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(check_set_log_level),
|
||||
cmocka_unit_test(check_set_auth_callback),
|
||||
cmocka_unit_test_setup_teardown(check_logging, setup, teardown),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -48,10 +48,10 @@ static void check_csync_normalize_etag(void **state)
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test(check_csync_normalize_etag),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(check_csync_normalize_etag),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#define TESTDB "/tmp/check_csync1/test.db"
|
||||
|
||||
static void setup(void **state) {
|
||||
static int setup(void **state) {
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
|
||||
@@ -47,9 +47,11 @@ static void setup(void **state) {
|
||||
|
||||
rc = sqlite3_close(db);
|
||||
assert_int_equal(rc, SQLITE_OK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void teardown(void **state) {
|
||||
static int teardown(void **state) {
|
||||
CSYNC *csync = *state;
|
||||
int rc;
|
||||
|
||||
@@ -60,6 +62,8 @@ static void teardown(void **state) {
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_csync_statedb_load(void **state)
|
||||
@@ -116,11 +120,11 @@ static void check_csync_statedb_close(void **state)
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test_setup_teardown(check_csync_statedb_load, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_statedb_close, setup, teardown),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(check_csync_statedb_load, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_statedb_close, setup, teardown),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
|
||||
|
||||
static void setup(void **state)
|
||||
static int setup(void **state)
|
||||
{
|
||||
CSYNC *csync;
|
||||
int rc = 0;
|
||||
@@ -39,7 +39,7 @@ static void setup(void **state)
|
||||
rc = system("mkdir -p /tmp/check_csync");
|
||||
assert_int_equal(rc, 0);
|
||||
csync_create(&csync, "/tmp/check_csync1");
|
||||
csync_init(csync);
|
||||
csync_init(csync, TESTDB);
|
||||
|
||||
sqlite3 *db = NULL;
|
||||
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
|
||||
@@ -51,9 +51,11 @@ static void setup(void **state)
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = csync;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_db(void **state)
|
||||
static int setup_db(void **state)
|
||||
{
|
||||
char *errmsg;
|
||||
int rc = 0;
|
||||
@@ -89,10 +91,12 @@ static void setup_db(void **state)
|
||||
assert_int_equal(rc, SQLITE_OK);
|
||||
|
||||
sqlite3_close(db);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void teardown(void **state) {
|
||||
static int teardown(void **state) {
|
||||
CSYNC *csync = *state;
|
||||
int rc = 0;
|
||||
|
||||
@@ -104,6 +108,8 @@ static void teardown(void **state) {
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -204,15 +210,15 @@ static void check_csync_statedb_get_stat_by_inode_not_found(void **state)
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test_setup_teardown(check_csync_statedb_query_statement, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_statedb_drop_tables, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_statedb_insert_metadata, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_statedb_write, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_statedb_get_stat_by_hash_not_found, setup_db, teardown),
|
||||
unit_test_setup_teardown(check_csync_statedb_get_stat_by_inode_not_found, setup_db, teardown),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(check_csync_statedb_query_statement, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_statedb_drop_tables, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_statedb_insert_metadata, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_statedb_write, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_statedb_get_stat_by_hash_not_found, setup_db, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_statedb_get_stat_by_inode_not_found, setup_db, teardown),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ static void statedb_insert_metadata(sqlite3 *db)
|
||||
}
|
||||
}
|
||||
|
||||
static void setup(void **state)
|
||||
static int setup(void **state)
|
||||
{
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
@@ -92,7 +92,7 @@ static void setup(void **state)
|
||||
rc = system("mkdir -p /tmp/check_csync1");
|
||||
assert_int_equal(rc, 0);
|
||||
csync_create(&csync, "/tmp/check_csync1");
|
||||
csync_init(csync);
|
||||
csync_init(csync, TESTDB);
|
||||
|
||||
/* Create a new db with metadata */
|
||||
sqlite3 *db;
|
||||
@@ -109,9 +109,11 @@ static void setup(void **state)
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = csync;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_ftw(void **state)
|
||||
static int setup_ftw(void **state)
|
||||
{
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
@@ -121,7 +123,7 @@ static void setup_ftw(void **state)
|
||||
rc = system("mkdir -p /tmp/check_csync1");
|
||||
assert_int_equal(rc, 0);
|
||||
csync_create(&csync, "/tmp");
|
||||
csync_init(csync);
|
||||
csync_init(csync, TESTDB);
|
||||
|
||||
sqlite3 *db = NULL;
|
||||
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
|
||||
@@ -135,9 +137,11 @@ static void setup_ftw(void **state)
|
||||
|
||||
csync->statedb.file = c_strdup( TESTDB );
|
||||
*state = csync;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void teardown(void **state)
|
||||
static int teardown(void **state)
|
||||
{
|
||||
CSYNC *csync = *state;
|
||||
int rc;
|
||||
@@ -147,9 +151,11 @@ static void teardown(void **state)
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void teardown_rm(void **state) {
|
||||
static int teardown_rm(void **state) {
|
||||
int rc;
|
||||
|
||||
teardown(state);
|
||||
@@ -158,6 +164,8 @@ static void teardown_rm(void **state) {
|
||||
assert_int_equal(rc, 0);
|
||||
rc = system("rm -rf /tmp/check_csync1");
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* create a file stat, caller must free memory */
|
||||
@@ -424,19 +432,19 @@ static void check_csync_ftw_failing_fn(void **state)
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test_setup_teardown(check_csync_detect_update, setup, teardown_rm),
|
||||
unit_test_setup_teardown(check_csync_detect_update_db_none, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_detect_update_db_eval, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_detect_update_db_rename, setup, teardown),
|
||||
unit_test_setup_teardown(check_csync_detect_update_db_new, setup, teardown_rm),
|
||||
unit_test_setup_teardown(check_csync_detect_update_null, setup, teardown_rm),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update, setup, teardown_rm),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_db_none, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_db_eval, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_db_rename, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_db_new, setup, teardown_rm),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_null, setup, teardown_rm),
|
||||
|
||||
unit_test_setup_teardown(check_csync_ftw, setup_ftw, teardown_rm),
|
||||
unit_test_setup_teardown(check_csync_ftw_empty_uri, setup_ftw, teardown_rm),
|
||||
unit_test_setup_teardown(check_csync_ftw_failing_fn, setup_ftw, teardown_rm),
|
||||
cmocka_unit_test_setup_teardown(check_csync_ftw, setup_ftw, teardown_rm),
|
||||
cmocka_unit_test_setup_teardown(check_csync_ftw_empty_uri, setup_ftw, teardown_rm),
|
||||
cmocka_unit_test_setup_teardown(check_csync_ftw_failing_fn, setup_ftw, teardown_rm),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,11 +43,11 @@ static void check_csync_memstat(void **state)
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
unit_test(check_csync_instruction_str),
|
||||
unit_test(check_csync_memstat),
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(check_csync_instruction_str),
|
||||
cmocka_unit_test(check_csync_memstat),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -457,6 +457,7 @@ sub traverse( $$;$ )
|
||||
|
||||
$isHere = 1 if( $acceptConflicts && !$isHere && $f =~ /_conflict/ );
|
||||
$isHere = 1 if( $f =~ /\.csync/ );
|
||||
$isHere = 1 if( $f =~ /\._sync_/ );
|
||||
assert( $isHere, "Filename local, but not remote: $f" );
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ assertLocalAndRemoteDir( 'remoteToLocal1', 1);
|
||||
|
||||
printInfo("simulate a owncloud 5 update by removing all the fileid");
|
||||
## simulate a owncloud 5 update by removing all the fileid
|
||||
system( "sqlite3 " . localDir() . ".csync_journal.db \"UPDATE metadata SET fileid='';\"");
|
||||
system( "sqlite3 " . localDir() . "._sync_*.db \"UPDATE metadata SET fileid='';\"");
|
||||
#refresh the ids
|
||||
csync();
|
||||
assertLocalAndRemoteDir( 'remoteToLocal1', 1);
|
||||
|
||||
@@ -61,7 +61,7 @@ sub getETagFromJournal($$)
|
||||
{
|
||||
my ($name,$num) = @_;
|
||||
|
||||
my $sql = "sqlite3 " . localDir() . ".csync_journal.db \"SELECT md5 FROM metadata WHERE path='$name';\"";
|
||||
my $sql = "sqlite3 " . localDir() . "._sync_*.db \"SELECT md5 FROM metadata WHERE path='$name';\"";
|
||||
open(my $fh, '-|', $sql) or die $!;
|
||||
my $etag = <$fh>;
|
||||
close $fh;
|
||||
|
||||
@@ -37,8 +37,8 @@ sub assertCsyncJournalOk {
|
||||
my $path = $_[0];
|
||||
|
||||
# FIXME: should test also remoteperm but it's not working with owncloud6
|
||||
# my $cmd = 'sqlite3 ' . $path . '.csync_journal.db "SELECT count(*) from metadata where length(remotePerm) == 0 or length(fileId) == 0"';
|
||||
my $cmd = 'sqlite3 ' . $path . '.csync_journal.db "SELECT count(*) from metadata where length(fileId) == 0"';
|
||||
# my $cmd = 'sqlite3 ' . $path . '._sync_*.db "SELECT count(*) from metadata where length(remotePerm) == 0 or length(fileId) == 0"';
|
||||
my $cmd = 'sqlite3 ' . $path . '._sync_*.db "SELECT count(*) from metadata where length(fileId) == 0"';
|
||||
my $result = `$cmd`;
|
||||
assert($result == "0");
|
||||
}
|
||||
@@ -170,14 +170,14 @@ assertLocalAndRemoteDir( '', 0);
|
||||
|
||||
#######################################################################
|
||||
printInfo( "move a directory in a outside read only folder" );
|
||||
system("sqlite3 " . localDir().'.csync_journal.db .dump');
|
||||
system("sqlite3 " . localDir().'._sync_*.db .dump');
|
||||
|
||||
#Missing directory should be restored
|
||||
#new directory should be uploaded
|
||||
system("mv " . localDir().'readonlyDirectory_PERM_M_/subdir_PERM_CK_ ' . localDir().'normalDirectory_PERM_CKDNV_/subdir_PERM_CKDNV_' );
|
||||
|
||||
csync();
|
||||
system("sqlite3 " . localDir().'.csync_journal.db .dump');
|
||||
system("sqlite3 " . localDir().'._sync_*.db .dump');
|
||||
assertCsyncJournalOk(localDir());
|
||||
|
||||
# old name restored
|
||||
@@ -229,7 +229,7 @@ system("rm -r " . localDir(). "readonlyDirectory_PERM_M_/moved_PERM_CK_");
|
||||
|
||||
assertLocalAndRemoteDir( '', 0);
|
||||
|
||||
system("sqlite3 " . localDir().'.csync_journal.db .dump');
|
||||
system("sqlite3 " . localDir().'._sync_*.db .dump');
|
||||
|
||||
|
||||
#######################################################################
|
||||
|
||||
@@ -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
|
||||
@@ -153,7 +153,8 @@ By default, the ownCloud Client ignores the following files:
|
||||
|
||||
* Files matched by one of the patterns defined in the Ignored Files Editor
|
||||
* Files containing characters that do not work on certain file systems ``(`\, /, :, ?, *, ", >, <, |`)``.
|
||||
* Files starting with ``.csync_journal.db``, as these files are reserved for journalling.
|
||||
* Files starting with ``._sync_xxxxxxx.db`` and the old format ``.csync_journal.db``,
|
||||
as these files are reserved for journalling.
|
||||
|
||||
If a pattern selected using a checkbox in the `ignoredFilesEditor-label` (or if
|
||||
a line in the exclude file starts with the character ``]`` directly followed by
|
||||
|
||||
+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``.
|
||||
|
||||
|
||||
+13
-17
@@ -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
|
||||
@@ -68,42 +68,38 @@ To set up your build environment for development using HomeBrew_:
|
||||
|
||||
1. Install Xcode
|
||||
2. Install Xcode command line tools::
|
||||
<<<<<<< HEAD
|
||||
xcode-select --install
|
||||
|
||||
3. Install homebrew::
|
||||
=======
|
||||
|
||||
xcode-select --install
|
||||
|
||||
3. Install homebrew::
|
||||
|
||||
>>>>>>> ca9ec4625391ae23940b3a62aaa0afe89f3d98e8
|
||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
|
||||
4. Add the ownCloud repository using the following command::
|
||||
|
||||
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/qmake
|
||||
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.
|
||||
|
||||
@@ -65,7 +65,7 @@ If no client is configured, or if you choose to use a different user to synchron
|
||||
you can specify the user
|
||||
password setting with the usual URL pattern. For example::
|
||||
|
||||
$ owncloudcmd / https://carla:secret@server/owncloud/remote.php/webdav/
|
||||
$ owncloudcmd /home/user/my_sync_folder https://carla:secret@server/owncloud/remote.php/webdav/
|
||||
|
||||
To synchronize the ownCloud directory ``Music`` to the local directory
|
||||
``media/music``, through a proxy listening on port ``8080``, and on a gateway
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -547,6 +547,111 @@ X-GNOME-Autostart-Delay=3
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# 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
|
||||
@@ -682,7 +787,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)
|
||||
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Pierre MOREAU <p.moreau@agim.idshost.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file p12topem.cpp
|
||||
* \brief Static library to convert p12 to pem
|
||||
* \author Pierre MOREAU <p.moreau@agim.idshost.fr>
|
||||
* \version 1.0.0
|
||||
* \date 09 January 2014
|
||||
*/
|
||||
|
||||
#include "p12topem.h"
|
||||
|
||||
/**
|
||||
* \fn string x509ToString (BIO)
|
||||
* \brief Return string from BIO SSL
|
||||
* \param BIO o PEM_write_BIO_...
|
||||
* \return string PEM
|
||||
*/
|
||||
string x509ToString(BIO *o) {
|
||||
BUF_MEM *bptr;
|
||||
BIO_get_mem_ptr(o, &bptr);
|
||||
int len = bptr->length;
|
||||
void* data = calloc(len+10, sizeof(char));
|
||||
BIO_read(o, data, len);
|
||||
string ret = std::string(static_cast<char*>(data));
|
||||
free(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \fn resultP12ToPem p12ToPem (string, string)
|
||||
* \brief Convert P12 to PEM
|
||||
* \param string p12File Path to P12 file
|
||||
* \param string p12Passwd Password to open P12 file
|
||||
* \return result (bool ReturnCode, Int ErrorCode, String Comment, String PrivateKey, String Certificate)
|
||||
*/
|
||||
resultP12ToPem p12ToPem(string p12File, string p12Passwd) {
|
||||
FILE *fp;
|
||||
PKCS12 *p12 = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
X509 *cert = NULL;
|
||||
STACK_OF(X509) *ca = NULL;
|
||||
|
||||
BIO *o = BIO_new(BIO_s_mem());
|
||||
|
||||
string privateKey = "";
|
||||
string certificate = "";
|
||||
|
||||
resultP12ToPem ret;
|
||||
ret.ReturnCode = false;
|
||||
ret.ErrorCode = 0;
|
||||
ret.Comment = "";
|
||||
ret.PrivateKey = "";
|
||||
ret.Certificate = "";
|
||||
|
||||
SSLeay_add_all_algorithms();
|
||||
ERR_load_crypto_strings();
|
||||
if(!(fp = fopen(p12File.c_str(), "rb"))) {
|
||||
ret.ErrorCode = 1;
|
||||
ret.Comment = strerror(errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
p12 = d2i_PKCS12_fp(fp, &p12);
|
||||
fclose (fp);
|
||||
|
||||
if (!p12) {
|
||||
ret.ErrorCode = 2;
|
||||
ret.Comment = "Unable to open PKCS#12 file";
|
||||
return ret;
|
||||
}
|
||||
if (!PKCS12_parse(p12, p12Passwd.c_str(), &pkey, &cert, &ca)) {
|
||||
ret.ErrorCode = 3;
|
||||
ret.Comment = "Unable to parse PKCS#12 file (wrong password ?)";
|
||||
return ret;
|
||||
}
|
||||
PKCS12_free(p12);
|
||||
|
||||
if (!(pkey && cert)) {
|
||||
ret.ErrorCode = 4;
|
||||
ret.Comment = "Certificate and/or key file doesn't exists";
|
||||
} else {
|
||||
PEM_write_bio_PrivateKey(o, pkey, 0, 0, 0, NULL, 0);
|
||||
privateKey = x509ToString(o);
|
||||
|
||||
PEM_write_bio_X509(o, cert);
|
||||
certificate = x509ToString(o);
|
||||
|
||||
BIO_free(o);
|
||||
|
||||
ret.ReturnCode = true;
|
||||
ret.ErrorCode = 0;
|
||||
ret.Comment = "All is fine";
|
||||
ret.PrivateKey = privateKey;
|
||||
ret.Certificate = certificate;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Pierre MOREAU <p.moreau@agim.idshost.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef P12TOPEM_H
|
||||
#define P12TOPEM_H
|
||||
|
||||
/**
|
||||
* \file p12topem.h
|
||||
* \brief Static library to convert p12 to pem
|
||||
* \author Pierre MOREAU <p.moreau@agim.idshost.fr>
|
||||
* \version 1.0.0
|
||||
* \date 09 January 2014
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pkcs12.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* \struct resultP12ToPem p12topem.h
|
||||
*/
|
||||
struct resultP12ToPem {
|
||||
bool ReturnCode;
|
||||
int ErrorCode;
|
||||
string Comment;
|
||||
string PrivateKey;
|
||||
string Certificate;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return string from BIO SSL
|
||||
* \param BIO o PEM_write_BIO_...
|
||||
* \return string PEM
|
||||
*/
|
||||
string x509ToString(BIO *o);
|
||||
|
||||
/**
|
||||
* \brief Convert P12 to PEM
|
||||
* \param string p12File Path to P12 file
|
||||
* \param string p12Passwd Password to open P12 file
|
||||
* \return result (bool ReturnCode, Int ErrorCode, String Comment, String PrivateKey, String Certificate)
|
||||
*/
|
||||
resultP12ToPem p12ToPem(string p12File, string p12Passwd);
|
||||
|
||||
#endif /* P12TOPEM_H */
|
||||
|
||||
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
|
||||
|
||||
+16
-6
@@ -121,7 +121,7 @@ QString queryPassword(const QString &user)
|
||||
class HttpCredentialsText : public HttpCredentials {
|
||||
public:
|
||||
HttpCredentialsText(const QString& user, const QString& password)
|
||||
: HttpCredentials(user, password, "", ""), // FIXME: not working with client certs yet (qknight)
|
||||
: HttpCredentials(user, password), // FIXME: not working with client certs yet (qknight)
|
||||
_sslTrusted(false)
|
||||
{}
|
||||
|
||||
@@ -278,7 +278,6 @@ void selectiveSyncFixup(OCC::SyncJournalDb *journal, const QStringList &newList)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
@@ -374,11 +373,20 @@ int main(int argc, char **argv) {
|
||||
QByteArray remUrl = options.target_url.toUtf8();
|
||||
|
||||
// Find the folder and the original owncloud url
|
||||
QStringList splitted = url.path().split(account->davPath());
|
||||
QStringList splitted = url.path().split("/" + account->davPath());
|
||||
url.setPath(splitted.value(0));
|
||||
|
||||
url.setScheme(url.scheme().replace("owncloud", "http"));
|
||||
QString folder = splitted.value(1);
|
||||
|
||||
QUrl credentialFreeUrl = url;
|
||||
credentialFreeUrl.setUserName(QString());
|
||||
credentialFreeUrl.setPassword(QString());
|
||||
|
||||
// Remote folders typically start with a / and don't end with one
|
||||
QString folder = "/" + splitted.value(1);
|
||||
if (folder.endsWith("/") && folder != "/") {
|
||||
folder.chop(1);
|
||||
}
|
||||
|
||||
SimpleSslErrorHandler *sslErrorHandler = new SimpleSslErrorHandler;
|
||||
|
||||
@@ -461,7 +469,9 @@ restart_sync:
|
||||
}
|
||||
|
||||
Cmd cmd;
|
||||
SyncJournalDb db(options.source_dir);
|
||||
QString dbPath = options.source_dir + SyncJournalDb::makeDbName(credentialFreeUrl, folder, user);
|
||||
SyncJournalDb db(dbPath);
|
||||
|
||||
if (!selectiveSyncList.empty()) {
|
||||
selectiveSyncFixup(&db, selectiveSyncList);
|
||||
}
|
||||
@@ -497,7 +507,7 @@ restart_sync:
|
||||
|
||||
app.exec();
|
||||
|
||||
if (engine.isAnotherSyncNeeded()) {
|
||||
if (engine.isAnotherSyncNeeded() != NoFollowUpSync) {
|
||||
if (restartCount < options.restartTimes) {
|
||||
restartCount++;
|
||||
qDebug() << "Restarting Sync, because another sync is needed" << restartCount;
|
||||
|
||||
@@ -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} )
|
||||
@@ -151,7 +153,6 @@ set(3rdparty_SRC
|
||||
../3rdparty/qtsingleapplication/qtlocalpeer.cpp
|
||||
../3rdparty/qtsingleapplication/qtsingleapplication.cpp
|
||||
../3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp
|
||||
../3rdparty/certificates/p12topem.cpp
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
|
||||
@@ -214,6 +214,17 @@ AccountPtr AccountManager::loadAccountHelper(QSettings& settings)
|
||||
auto acc = createAccount();
|
||||
|
||||
QString authType = settings.value(QLatin1String(authTypeC)).toString();
|
||||
|
||||
// There was an account-type saving bug when 'skip folder config' was used
|
||||
// See #5408. This attempts to fix up the "dummy" authType
|
||||
if (authType == QLatin1String("dummy")) {
|
||||
if (settings.contains(QLatin1String("http_user"))) {
|
||||
authType = "http";
|
||||
} else if (settings.contains(QLatin1String("shibboleth_shib_user"))) {
|
||||
authType = "shibboleth";
|
||||
}
|
||||
}
|
||||
|
||||
QString overrideUrl = Theme::instance()->overrideServerUrl();
|
||||
QString forceAuth = Theme::instance()->forceConfigAuthType();
|
||||
if(!forceAuth.isEmpty() && !overrideUrl.isEmpty() ) {
|
||||
@@ -224,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
|
||||
@@ -237,7 +251,7 @@ AccountPtr AccountManager::loadAccountHelper(QSettings& settings)
|
||||
|
||||
acc->setCredentials(CredentialsFactory::create(authType));
|
||||
|
||||
// now the cert, it is in the general group
|
||||
// now the server cert, it is in the general group
|
||||
settings.beginGroup(QLatin1String("General"));
|
||||
acc->setApprovedCerts(QSslCertificate::fromData(settings.value(caCertsKeyC).toByteArray()));
|
||||
settings.endGroup();
|
||||
|
||||
@@ -214,8 +214,10 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||
}
|
||||
|
||||
tv->setCurrentIndex(index);
|
||||
QString alias = _model->data( index, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
bool folderPaused = _model->data( index, FolderStatusDelegate::FolderSyncPaused).toBool();
|
||||
bool folderConnected = _model->data( index, FolderStatusDelegate::FolderAccountConnected ).toBool();
|
||||
auto folderMan = FolderMan::instance();
|
||||
|
||||
QMenu *menu = new QMenu(tv);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
@@ -231,6 +233,10 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||
|
||||
if (!folderPaused) {
|
||||
ac = menu->addAction(tr("Force sync now"));
|
||||
if (folderMan->currentSyncFolder() == folderMan->folder(alias)) {
|
||||
ac->setText(tr("Restart sync"));
|
||||
}
|
||||
ac->setEnabled(folderConnected);
|
||||
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotForceSyncCurrentFolder()));
|
||||
}
|
||||
|
||||
@@ -295,7 +301,8 @@ void AccountSettings::slotFolderWizardAccepted()
|
||||
FolderDefinition definition;
|
||||
definition.localPath = FolderDefinition::prepareLocalPath(
|
||||
folderWizard->field(QLatin1String("sourceFolder")).toString());
|
||||
definition.targetPath = folderWizard->property("targetPath").toString();
|
||||
definition.targetPath = FolderDefinition::prepareTargetPath(
|
||||
folderWizard->property("targetPath").toString());
|
||||
|
||||
{
|
||||
QDir dir(definition.localPath);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>462</width>
|
||||
<height>186</height>
|
||||
<height>188</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -32,7 +32,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelCertificateFile">
|
||||
<property name="text">
|
||||
<string>Certificate :</string>
|
||||
<string>Certificate & Key (pkcs12) :</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -347,14 +347,6 @@ void Application::slotownCloudWizardDone( int res )
|
||||
}
|
||||
}
|
||||
|
||||
static void csyncLogCatcher(int /*verbosity*/,
|
||||
const char */*function*/,
|
||||
const char *buffer,
|
||||
void */*userdata*/)
|
||||
{
|
||||
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
||||
}
|
||||
|
||||
void Application::setupLogging()
|
||||
{
|
||||
// might be called from second instance
|
||||
@@ -370,10 +362,6 @@ void Application::setupLogging()
|
||||
.arg(property("ui_lang").toString())
|
||||
.arg(_theme->version())
|
||||
.arg(Utility::platformName());
|
||||
|
||||
// Setup CSYNC logging to forward to our own logger
|
||||
csync_set_log_callback( csyncLogCatcher );
|
||||
csync_set_log_level( Logger::instance()->isNoop() ? 0 : 11 );
|
||||
}
|
||||
|
||||
void Application::slotUseMonoIconsChanged(bool)
|
||||
|
||||
@@ -27,7 +27,7 @@ class HttpCredentialsGui : public HttpCredentials {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HttpCredentialsGui() : HttpCredentials() {}
|
||||
HttpCredentialsGui(const QString& user, const QString& password, const QString& certificatePath, const QString& certificatePasswd) : HttpCredentials(user, password, certificatePath, certificatePasswd) {}
|
||||
HttpCredentialsGui(const QString& user, const QString& password, const QSslCertificate& certificate, const QSslKey& key) : HttpCredentials(user, password, certificate, key) {}
|
||||
void askFromUser() Q_DECL_OVERRIDE;
|
||||
Q_INVOKABLE void askFromUserAsync();
|
||||
|
||||
|
||||
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