diff --git a/.htaccess b/.htaccess
index 81dae47a34..190b414558 100644
--- a/.htaccess
+++ b/.htaccess
@@ -23,7 +23,7 @@
Header set Cache-Control "max-age=15778463"
-
+
# Let browsers cache WOFF files for a week
Header set Cache-Control "max-age=604800"
diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php
index b9ef447b74..8a8a2f100e 100644
--- a/lib/private/DB/Connection.php
+++ b/lib/private/DB/Connection.php
@@ -401,7 +401,7 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
}
/**
- * Espace a parameter to be used in a LIKE query
+ * Escape a parameter to be used in a LIKE query
*
* @param string $param
* @return string
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php
index a32ae4a182..154f2447c7 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php
@@ -26,6 +26,7 @@ namespace OC\DB\QueryBuilder\ExpressionBuilder;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder as DoctrineExpressionBuilder;
use OC\DB\QueryBuilder\CompositeExpression;
+use OC\DB\QueryBuilder\FunctionBuilder\FunctionBuilder;
use OC\DB\QueryBuilder\Literal;
use OC\DB\QueryBuilder\QueryFunction;
use OC\DB\QueryBuilder\QuoteHelper;
@@ -45,15 +46,20 @@ class ExpressionBuilder implements IExpressionBuilder {
/** @var IDBConnection */
protected $connection;
+ /** @var FunctionBuilder */
+ protected $functionBuilder;
+
/**
* Initializes a new ExpressionBuilder.
*
- * @param \OCP\IDBConnection $connection
+ * @param IDBConnection $connection
+ * @param IQueryBuilder $queryBuilder
*/
- public function __construct(IDBConnection $connection) {
+ public function __construct(IDBConnection $connection, IQueryBuilder $queryBuilder) {
$this->connection = $connection;
$this->helper = new QuoteHelper();
$this->expressionBuilder = new DoctrineExpressionBuilder($connection);
+ $this->functionBuilder = $queryBuilder->func();
}
/**
@@ -298,9 +304,7 @@ class ExpressionBuilder implements IExpressionBuilder {
* @since 9.0.0
*/
public function iLike($x, $y, $type = null) {
- $x = $this->helper->quoteColumnName($x);
- $y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->comparison("LOWER($x)", 'LIKE', "LOWER($y)");
+ return $this->expressionBuilder->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y));
}
/**
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php
index 17f7fd5aa4..aa7ef8e70b 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php
@@ -25,6 +25,7 @@ namespace OC\DB\QueryBuilder\ExpressionBuilder;
use OC\DB\Connection;
+use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
class MySqlExpressionBuilder extends ExpressionBuilder {
@@ -34,9 +35,10 @@ class MySqlExpressionBuilder extends ExpressionBuilder {
/**
* @param \OCP\IDBConnection|Connection $connection
+ * @param IQueryBuilder $queryBuilder
*/
- public function __construct(IDBConnection $connection) {
- parent::__construct($connection);
+ public function __construct(IDBConnection $connection, IQueryBuilder $queryBuilder) {
+ parent::__construct($connection, $queryBuilder);
$params = $connection->getParams();
$this->charset = isset($params['charset']) ? $params['charset'] : 'utf8';
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
index 14d8ad33e4..d9e8a1279c 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
@@ -31,4 +31,8 @@ class SqliteExpressionBuilder extends ExpressionBuilder {
public function like($x, $y, $type = null) {
return parent::like($x, $y, $type) . " ESCAPE '\\'";
}
+
+ public function iLike($x, $y, $type = null) {
+ return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y), $type);
+ }
}
diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php
index 2789b8cb35..1d74530635 100644
--- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php
+++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php
@@ -59,4 +59,8 @@ class FunctionBuilder implements IFunctionBuilder {
public function sum($field) {
return new QueryFunction('SUM(' . $this->helper->quoteColumnName($field) . ')');
}
+
+ public function lower($field) {
+ return new QueryFunction('LOWER(' . $this->helper->quoteColumnName($field) . ')');
+ }
}
diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php
index 58be4b4325..d6f8bb48ac 100644
--- a/lib/private/DB/QueryBuilder/QueryBuilder.php
+++ b/lib/private/DB/QueryBuilder/QueryBuilder.php
@@ -112,15 +112,15 @@ class QueryBuilder implements IQueryBuilder {
*/
public function expr() {
if ($this->connection instanceof OracleConnection) {
- return new OCIExpressionBuilder($this->connection);
+ return new OCIExpressionBuilder($this->connection, $this);
} else if ($this->connection->getDatabasePlatform() instanceof PostgreSqlPlatform) {
- return new PgSqlExpressionBuilder($this->connection);
+ return new PgSqlExpressionBuilder($this->connection, $this);
} else if ($this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
- return new MySqlExpressionBuilder($this->connection);
+ return new MySqlExpressionBuilder($this->connection, $this);
} else if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
- return new SqliteExpressionBuilder($this->connection);
+ return new SqliteExpressionBuilder($this->connection, $this);
} else {
- return new ExpressionBuilder($this->connection);
+ return new ExpressionBuilder($this->connection, $this);
}
}
diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php
index 471ff1f45a..6e44c90228 100644
--- a/lib/private/User/Database.php
+++ b/lib/private/User/Database.php
@@ -40,6 +40,7 @@
* along with this program. If not, see
*
*/
+
/*
*
* The following SQL statement is just a help for developers and will not be
@@ -56,6 +57,7 @@
namespace OC\User;
use OC\Cache\CappedMemoryCache;
+use OC\DB\QueryBuilder\Literal;
use OCP\IUserBackend;
use OCP\Util;
use Symfony\Component\EventDispatcher\EventDispatcher;
@@ -83,6 +85,7 @@ class Database extends Backend implements IUserBackend {
/**
* Create a new user
+ *
* @param string $uid The username of the user to create
* @param string $password The password of the new user
* @return bool
@@ -112,6 +115,7 @@ class Database extends Backend implements IUserBackend {
/**
* delete a user
+ *
* @param string $uid The username of the user to delete
* @return bool
*
@@ -131,6 +135,7 @@ class Database extends Backend implements IUserBackend {
/**
* Set password
+ *
* @param string $uid The username
* @param string $password The new password
* @return bool
@@ -152,6 +157,7 @@ class Database extends Backend implements IUserBackend {
/**
* Set display name
+ *
* @param string $uid The username
* @param string $displayName The new display name
* @return bool
@@ -172,6 +178,7 @@ class Database extends Backend implements IUserBackend {
/**
* get display name of the user
+ *
* @param string $uid user ID of the user
* @return string display name
*/
@@ -189,20 +196,29 @@ class Database extends Backend implements IUserBackend {
* @return array an array of all displayNames (value) and the corresponding uids (key)
*/
public function getDisplayNames($search = '', $limit = null, $offset = null) {
- $parameters = [];
- $searchLike = '';
- if ($search !== '') {
- $parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%';
- $parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%';
- $searchLike = ' WHERE LOWER(`displayname`) LIKE LOWER(?) OR '
- . 'LOWER(`uid`) LIKE LOWER(?)';
- }
+ $connection = \OC::$server->getDatabaseConnection();
- $displayNames = array();
- $query = \OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users`'
- . $searchLike .' ORDER BY LOWER(`displayname`), LOWER(`uid`) ASC', $limit, $offset);
- $result = $query->execute($parameters);
- while ($row = $result->fetchRow()) {
+ $query = $connection->getQueryBuilder();
+
+ $query->select('uid', 'displayname')
+ ->from('users', 'u')
+ ->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
+ $query->expr()->eq('userid', 'uid')),
+ $query->expr()->eq('appid', new Literal('settings')),
+ $query->expr()->eq('configkey', new Literal('email'))
+ )
+ // sqlite doesn't like re-using a single named parameter here
+ ->where($query->expr()->iLike('uid', $query->createPositionalParameter('%' . $connection->escapeLikeParameter($search) . '%')))
+ ->orWhere($query->expr()->iLike('displayname', $query->createPositionalParameter('%' . $connection->escapeLikeParameter($search) . '%')))
+ ->orWhere($query->expr()->iLike('configvalue', $query->createPositionalParameter('%' . $connection->escapeLikeParameter($search) . '%')))
+ ->orderBy($query->func()->lower('displayname'), 'ASC')
+ ->orderBy($query->func()->lower('uid'), 'ASC')
+ ->setMaxResults($limit)
+ ->setFirstResult($offset);
+
+ $result = $query->execute();
+ $displayNames = [];
+ while ($row = $result->fetch()) {
$displayNames[$row['uid']] = $row['displayname'];
}
@@ -211,6 +227,7 @@ class Database extends Backend implements IUserBackend {
/**
* Check if the password is correct
+ *
* @param string $uid The username
* @param string $password The password
* @return string
@@ -226,8 +243,8 @@ class Database extends Backend implements IUserBackend {
if ($row) {
$storedHash = $row['password'];
$newHash = '';
- if(\OC::$server->getHasher()->verify($password, $storedHash, $newHash)) {
- if(!empty($newHash)) {
+ if (\OC::$server->getHasher()->verify($password, $storedHash, $newHash)) {
+ if (!empty($newHash)) {
$this->setPassword($uid, $password);
}
return $row['uid'];
@@ -240,15 +257,16 @@ class Database extends Backend implements IUserBackend {
/**
* Load an user in the cache
+ *
* @param string $uid the username
* @return boolean true if user was found, false otherwise
*/
private function loadUser($uid) {
- $uid = (string) $uid;
+ $uid = (string)$uid;
if (!isset($this->cache[$uid])) {
//guests $uid could be NULL or ''
if ($uid === '') {
- $this->cache[$uid]=false;
+ $this->cache[$uid] = false;
return true;
}
@@ -285,26 +303,15 @@ class Database extends Backend implements IUserBackend {
* @return string[] an array of all uids
*/
public function getUsers($search = '', $limit = null, $offset = null) {
- $parameters = [];
- $searchLike = '';
- if ($search !== '') {
- $parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%';
- $searchLike = ' WHERE LOWER(`uid`) LIKE LOWER(?)';
- $parameters[] = '%' . \OC::$server->getDatabaseConnection()->escapeLikeParameter($search) . '%';
- $searchLike .= ' OR LOWER(`displayname`) LIKE LOWER(?)';
- }
-
- $query = \OC_DB::prepare('SELECT `uid` FROM `*PREFIX*users`' . $searchLike . ' ORDER BY LOWER(`uid`) ASC', $limit, $offset);
- $result = $query->execute($parameters);
- $users = array();
- while ($row = $result->fetchRow()) {
- $users[] = $row['uid'];
- }
- return $users;
+ $users = $this->getDisplayNames($search, $limit, $offset);
+ $userIds = array_keys($users);
+ sort($userIds, SORT_STRING | SORT_FLAG_CASE);
+ return $userIds;
}
/**
* check if a user exists
+ *
* @param string $uid the username
* @return boolean
*/
@@ -315,6 +322,7 @@ class Database extends Backend implements IUserBackend {
/**
* get the user's home directory
+ *
* @param string $uid the username
* @return string|false
*/
@@ -364,14 +372,15 @@ class Database extends Backend implements IUserBackend {
/**
* Backend name to be shown in user management
+ *
* @return string the name of the backend to be shown
*/
- public function getBackendName(){
+ public function getBackendName() {
return 'Database';
}
public static function preLoginNameUsedAsUserName($param) {
- if(!isset($param['uid'])) {
+ if (!isset($param['uid'])) {
throw new \Exception('key uid is expected to be set in $param');
}
diff --git a/lib/public/DB/QueryBuilder/IFunctionBuilder.php b/lib/public/DB/QueryBuilder/IFunctionBuilder.php
index 478fd87940..b82749d542 100644
--- a/lib/public/DB/QueryBuilder/IFunctionBuilder.php
+++ b/lib/public/DB/QueryBuilder/IFunctionBuilder.php
@@ -71,4 +71,13 @@ interface IFunctionBuilder {
* @since 12.0.0
*/
public function sum($field);
+
+ /**
+ * Transforms a string field or value to lower case
+ *
+ * @param mixed $field
+ * @return IQueryFunction
+ * @since 13.0.0
+ */
+ public function lower($field);
}
diff --git a/tests/lib/DB/QueryBuilder/ExpressionBuilderTest.php b/tests/lib/DB/QueryBuilder/ExpressionBuilderTest.php
index ff58b3f6e0..1bf42d230f 100644
--- a/tests/lib/DB/QueryBuilder/ExpressionBuilderTest.php
+++ b/tests/lib/DB/QueryBuilder/ExpressionBuilderTest.php
@@ -48,7 +48,9 @@ class ExpressionBuilderTest extends TestCase {
$this->connection = \OC::$server->getDatabaseConnection();
- $this->expressionBuilder = new ExpressionBuilder($this->connection);
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+
+ $this->expressionBuilder = new ExpressionBuilder($this->connection, $queryBuilder);
$this->doctrineExpressionBuilder = new DoctrineExpressionBuilder($this->connection);
}
diff --git a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php
index e89da0fbc9..869faccc5c 100644
--- a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php
+++ b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php
@@ -79,4 +79,14 @@ class FunctionBuilderTest extends TestCase {
$this->assertEquals('oobar', $query->execute()->fetchColumn());
}
+
+ public function testLower() {
+ $query = $this->connection->getQueryBuilder();
+
+ $query->select($query->func()->lower($query->createNamedParameter('FooBar')));
+ $query->from('appconfig')
+ ->setMaxResults(1);
+
+ $this->assertEquals('foobar', $query->execute()->fetchColumn());
+ }
}
diff --git a/tests/lib/User/Backend.php b/tests/lib/User/Backend.php
index 85ccbac913..1c7d482c48 100644
--- a/tests/lib/User/Backend.php
+++ b/tests/lib/User/Backend.php
@@ -103,15 +103,23 @@ abstract class Backend extends \Test\TestCase {
$name1 = 'foobarbaz';
$name2 = 'bazbarfoo';
$name3 = 'notme';
+ $name4 = 'under_score';
$this->backend->createUser($name1, 'pass1');
$this->backend->createUser($name2, 'pass2');
$this->backend->createUser($name3, 'pass3');
+ $this->backend->createUser($name4, 'pass4');
$result = $this->backend->getUsers('bar');
- $this->assertSame(2, count($result));
+ $this->assertCount(2, $result);
$result = $this->backend->getDisplayNames('bar');
- $this->assertSame(2, count($result));
+ $this->assertCount(2, $result);
+
+ $result = $this->backend->getUsers('under_');
+ $this->assertCount(1, $result);
+
+ $result = $this->backend->getUsers('not_');
+ $this->assertCount(0, $result);
}
}
diff --git a/tests/lib/User/DatabaseTest.php b/tests/lib/User/DatabaseTest.php
index 0e6900651c..a6fb8047a9 100644
--- a/tests/lib/User/DatabaseTest.php
+++ b/tests/lib/User/DatabaseTest.php
@@ -24,6 +24,7 @@ namespace Test\User;
use OC\HintException;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\GenericEvent;
+use OC\User\User;
/**
* Class DatabaseTest
@@ -113,4 +114,39 @@ class DatabaseTest extends Backend {
$this->backend->createUser($user1, 'pw2');
$this->assertTrue($this->backend->userExists($user1));
}
+
+ public function testSearch() {
+ parent::testSearch();
+
+ $user1 = $this->getUser();
+ $this->backend->createUser($user1, 'pass1');
+
+ $user2 = $this->getUser();
+ $this->backend->createUser($user2, 'pass1');
+
+ $user1Obj = new User($user1, $this->backend);
+ $user2Obj = new User($user2, $this->backend);
+ $emailAddr1 = "$user1@nextcloud.com";
+ $emailAddr2 = "$user2@nextcloud.com";
+
+ $user1Obj->setDisplayName('User 1 Display');
+
+ $result = $this->backend->getDisplayNames('display');
+ $this->assertCount(1, $result);
+
+ $result = $this->backend->getDisplayNames(strtoupper($user1));
+ $this->assertCount(1, $result);
+
+ $user1Obj->setEMailAddress($emailAddr1);
+ $user2Obj->setEMailAddress($emailAddr2);
+
+ $result = $this->backend->getUsers('@nextcloud.com');
+ $this->assertCount(2, $result);
+
+ $result = $this->backend->getDisplayNames('@nextcloud.com');
+ $this->assertCount(2, $result);
+
+ $result = $this->backend->getDisplayNames('@nextcloud.COM');
+ $this->assertCount(2, $result);
+ }
}