diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php
index 1e54fc10ef..b8dc68f1f8 100644
--- a/apps/files/appinfo/remote.php
+++ b/apps/files/appinfo/remote.php
@@ -43,6 +43,7 @@ $server->setBaseUri($baseuri);
// Load plugins
$defaults = new OC_Defaults();
+$server->addPlugin(new \OC\Connector\Sabre\BlockLegacyClientPlugin(\OC::$server->getConfig()));
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $defaults->getName()));
// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to /
$server->addPlugin(new \OC\Connector\Sabre\DummyGetResponsePlugin());
diff --git a/config/config.sample.php b/config/config.sample.php
index 61ae59542d..45aaf6a107 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -799,6 +799,17 @@ $CONFIG = array(
*/
'cipher' => 'AES-256-CFB',
+/**
+ * The minimum ownCloud desktop client version that will be allowed to sync with
+ * this server instance. All connections made from earlier clients will be denied
+ * by the server. Defaults to the minimum officially supported ownCloud version at
+ * the time of release of this server version.
+ *
+ * When changing this, note that older unsupported versions of the ownCloud desktop
+ * client may not function as expected, and could lead to permanent data loss for
+ * clients or other unexpected results.
+ */
+'minimum.supported.desktop.version' => '1.7.0',
/**
* Memory caching backend configuration
diff --git a/lib/private/connector/sabre/blocklegacyclientplugin.php b/lib/private/connector/sabre/blocklegacyclientplugin.php
new file mode 100644
index 0000000000..9480cd1f06
--- /dev/null
+++ b/lib/private/connector/sabre/blocklegacyclientplugin.php
@@ -0,0 +1,79 @@
+
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+namespace OC\Connector\Sabre;
+
+use OCP\IConfig;
+use Sabre\HTTP\RequestInterface;
+use Sabre\DAV\ServerPlugin;
+use Sabre\DAV\Exception;
+
+/**
+ * Class BlockLegacyClientPlugin is used to detect old legacy sync clients and
+ * returns a 403 status to those clients
+ *
+ * @package OC\Connector\Sabre
+ */
+class BlockLegacyClientPlugin extends ServerPlugin {
+ /** @var \Sabre\DAV\Server */
+ protected $server;
+ /** @var IConfig */
+ protected $config;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config) {
+ $this->config = $config;
+ }
+
+ /**
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ public function initialize(\Sabre\DAV\Server $server) {
+ $this->server = $server;
+ $this->server->on('beforeMethod', [$this, 'beforeHandler'], 200);
+ }
+
+ /**
+ * Detects all unsupported clients and throws a \Sabre\DAV\Exception\Forbidden
+ * exception which will result in a 403 to them.
+ * @param RequestInterface $request
+ * @throws \Sabre\DAV\Exception\Forbidden If the client version is not supported
+ */
+ public function beforeHandler(RequestInterface $request) {
+ $userAgent = $request->getHeader('User-Agent');
+ if($userAgent === null) {
+ return;
+ }
+
+ $minimumSupportedDesktopVersion = $this->config->getSystemValue('minimum.supported.desktop.version', '1.7.0');
+
+ // Match on the mirall version which is in scheme "Mozilla/5.0 (%1) mirall/%2" or
+ // "mirall/%1" for older releases
+ preg_match("/(?:mirall\\/)([\d.]+)/i", $userAgent, $versionMatches);
+ if(isset($versionMatches[1]) &&
+ version_compare($versionMatches[1], $minimumSupportedDesktopVersion) === -1) {
+ throw new \Sabre\DAV\Exception\Forbidden('Unsupported client version.');
+ }
+ }
+}
diff --git a/tests/lib/connector/sabre/BlockLegacyClientPluginTest.php b/tests/lib/connector/sabre/BlockLegacyClientPluginTest.php
new file mode 100644
index 0000000000..05488d9716
--- /dev/null
+++ b/tests/lib/connector/sabre/BlockLegacyClientPluginTest.php
@@ -0,0 +1,129 @@
+
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+namespace Test\Connector\Sabre;
+
+use OC\Connector\Sabre\BlockLegacyClientPlugin;
+use Test\TestCase;
+use OCP\IConfig;
+
+/**
+ * Class BlockLegacyClientPluginTest
+ *
+ * @package Test\Connector\Sabre
+ */
+class BlockLegacyClientPluginTest extends TestCase {
+ /** @var IConfig */
+ private $config;
+ /** @var BlockLegacyClientPlugin */
+ private $blockLegacyClientVersionPlugin;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->config = $this->getMock('\OCP\IConfig');
+ $this->blockLegacyClientVersionPlugin = new BlockLegacyClientPlugin($this->config);
+ }
+
+ /**
+ * @return array
+ */
+ public function oldDesktopClientProvider() {
+ return [
+ ['Mozilla/5.0 (1.5.0) mirall/1.5.0'],
+ ['mirall/1.5.0'],
+ ['mirall/1.5.4'],
+ ['mirall/1.6.0'],
+ ['Mozilla/5.0 (Bogus Text) mirall/1.6.9'],
+ ];
+ }
+
+ /**
+ * @dataProvider oldDesktopClientProvider
+ * @param string $userAgent
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ * @expectedExceptionMessage Unsupported client version.
+ */
+ public function testBeforeHandlerException($userAgent) {
+ /** @var \Sabre\HTTP\RequestInterface $request */
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ $request
+ ->expects($this->once())
+ ->method('getHeader')
+ ->with('User-Agent')
+ ->will($this->returnValue($userAgent));
+
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('minimum.supported.desktop.version', '1.7.0')
+ ->will($this->returnValue('1.7.0'));
+
+ $this->blockLegacyClientVersionPlugin->beforeHandler($request);
+ }
+
+ /**
+ * @return array
+ */
+ public function newAndAlternateDesktopClientProvider() {
+ return [
+ ['Mozilla/5.0 (1.7.0) mirall/1.7.0'],
+ ['mirall/1.8.3'],
+ ['mirall/1.7.2'],
+ ['mirall/1.7.0'],
+ ['Mozilla/5.0 (Bogus Text) mirall/1.9.3'],
+ ];
+ }
+
+ /**
+ * @dataProvider newAndAlternateDesktopClientProvider
+ * @param string $userAgent
+ */
+ public function testBeforeHandlerSuccess($userAgent) {
+ /** @var \Sabre\HTTP\RequestInterface $request */
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ $request
+ ->expects($this->once())
+ ->method('getHeader')
+ ->with('User-Agent')
+ ->will($this->returnValue($userAgent));
+
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('minimum.supported.desktop.version', '1.7.0')
+ ->will($this->returnValue('1.7.0'));
+
+ $this->blockLegacyClientVersionPlugin->beforeHandler($request);
+ }
+
+ public function testBeforeHandlerNoUserAgent() {
+ /** @var \Sabre\HTTP\RequestInterface $request */
+ $request = $this->getMock('\Sabre\HTTP\RequestInterface');
+ $request
+ ->expects($this->once())
+ ->method('getHeader')
+ ->with('User-Agent')
+ ->will($this->returnValue(null));
+ $this->blockLegacyClientVersionPlugin->beforeHandler($request);
+ }
+
+}