diff --git a/src/ldap.js b/src/ldap.js index 0eb18b68f..afbff4f7d 100644 --- a/src/ldap.js +++ b/src/ldap.js @@ -288,14 +288,14 @@ function mailboxSearch(req, res, next) { } else if (req.dn.rdns[0].attrs.domain) { // legacy ldap mailbox search for old sogo var domain = req.dn.rdns[0].attrs.domain.value.toLowerCase(); - mailboxdb.listMailboxes(domain, 1, 1000, function (error, result) { + mailboxdb.listMailboxes(domain, 1, 1000, function (error, mailboxes) { if (error) return next(new ldap.OperationsError(error.toString())); var results = []; // send mailbox objects - result.forEach(function (mailbox) { + mailboxes.forEach(function (mailbox) { var dn = ldap.parseDN(`cn=${mailbox.name}@${domain},domain=${domain},ou=mailboxes,dc=cloudron`); var obj = { @@ -346,23 +346,19 @@ function mailboxSearch(req, res, next) { } }; - mailboxdb.getAliasesForName(mailbox.name, mailbox.domain, function (error, aliases) { - if (error) return callback(error); - - aliases.forEach(function (a, idx) { - obj.attributes['mail' + idx] = `${a.name}@${a.domain}`; - }); - - // ensure all filter values are also lowercase - var lowerCaseFilter = safe(function () { return ldap.parseFilter(req.filter.toString().toLowerCase()); }, null); - if (!lowerCaseFilter) return next(new ldap.OperationsError(safe.error.toString())); - - if ((req.dn.equals(dn) || req.dn.parentOf(dn)) && lowerCaseFilter.matches(obj.attributes)) { - results.push(obj); - } - - callback(); + mailbox.aliases.forEach(function (a, idx) { + obj.attributes['mail' + idx] = `${a.name}@${a.domain}`; }); + + // ensure all filter values are also lowercase + var lowerCaseFilter = safe(function () { return ldap.parseFilter(req.filter.toString().toLowerCase()); }, null); + if (!lowerCaseFilter) return next(new ldap.OperationsError(safe.error.toString())); + + if ((req.dn.equals(dn) || req.dn.parentOf(dn)) && lowerCaseFilter.matches(obj.attributes)) { + results.push(obj); + } + + callback(); }); }, function (error) { if (error) return next(new ldap.OperationsError(error.toString())); diff --git a/src/mailboxdb.js b/src/mailboxdb.js index 035144aad..572565b25 100644 --- a/src/mailboxdb.js +++ b/src/mailboxdb.js @@ -53,6 +53,16 @@ function postProcess(data) { return data; } +function postProcessAliases(data) { + const aliasNames = JSON.parse(data.aliasNames), aliasDomains = JSON.parse(data.aliasDomains); + delete data.aliasNames; + delete data.aliasDomains; + data.aliases = []; + for (let i = 0; i < aliasNames.length; i++) { // NOTE: aliasNames is [ null ] when no aliases + if (aliasNames[i]) data.aliases[i] = { name: aliasNames[i], domain: aliasDomains[i] }; + } +} + function addMailbox(name, domain, ownerId, ownerType, callback) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); @@ -225,14 +235,22 @@ function listMailboxes(domain, search, page, perPage, callback) { assert.strictEqual(typeof perPage, 'number'); assert.strictEqual(typeof callback, 'function'); - let query = `SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE type = ? AND domain = ?`; - if (search) query += ' AND (name LIKE ' + mysql.escape('%' + search + '%') + ')'; - query += 'ORDER BY name LIMIT ?,?'; + const escapedSearch = mysql.escape('%' + search + '%'); // this also quotes the string + const searchQuery = search ? ` HAVING (name LIKE ${escapedSearch} OR aliasNames LIKE ${escapedSearch} OR aliasDomains LIKE ${escapedSearch})` : ''; // having instead of where because of aggregated columns use - database.query(query, [ exports.TYPE_MAILBOX, domain, (page-1)*perPage, perPage ], function (error, results) { + const query = 'SELECT m1.name AS name, m1.domain AS domain, m1.ownerId AS ownerId, m1.ownerType as ownerType, JSON_ARRAYAGG(m2.name) AS aliasNames, JSON_ARRAYAGG(m2.domain) AS aliasDomains ' + + ` FROM (SELECT * FROM mailboxes WHERE type='${exports.TYPE_MAILBOX}') AS m1` + + ` LEFT JOIN (SELECT * FROM mailboxes WHERE type='${exports.TYPE_ALIAS}') AS m2` + + ' ON m1.name=m2.aliasName AND m1.domain=m2.aliasDomain AND m1.ownerId=m2.ownerId' + + ' WHERE m1.domain = ?' + + ' GROUP BY m1.name, m1.domain, m1.ownerId' + + searchQuery + + ' ORDER BY name LIMIT ?,?'; + + database.query(query, [ domain, (page-1)*perPage, perPage ], function (error, results) { if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); - results.forEach(function (result) { postProcess(result); }); + results.forEach(postProcessAliases); callback(null, results); }); @@ -243,14 +261,20 @@ function listAllMailboxes(page, perPage, callback) { assert.strictEqual(typeof perPage, 'number'); assert.strictEqual(typeof callback, 'function'); - database.query(`SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE type = ? ORDER BY name LIMIT ?,?`, - [ exports.TYPE_MAILBOX, (page-1)*perPage, perPage ], function (error, results) { - if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); + const query = 'SELECT m1.name AS name, m1.domain AS domain, m1.ownerId AS ownerId, m1.ownerType as ownerType, JSON_ARRAYAGG(m2.name) AS aliasNames, JSON_ARRAYAGG(m2.domain) AS aliasDomains ' + + ` FROM (SELECT * FROM mailboxes WHERE type='${exports.TYPE_MAILBOX}') AS m1` + + + ` LEFT JOIN (SELECT * FROM mailboxes WHERE type='${exports.TYPE_ALIAS}') AS m2` + + + ' ON m1.name=m2.aliasName AND m1.domain=m2.aliasDomain AND m1.ownerId=m2.ownerId' + + ' GROUP BY m1.name, m1.domain, m1.ownerId' + + ' ORDER BY name LIMIT ?,?'; - results.forEach(function (result) { postProcess(result); }); + database.query(query, [ (page-1)*perPage, perPage ], function (error, results) { + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); - callback(null, results); - }); + results.forEach(postProcessAliases); + + callback(null, results); + }); } function getLists(domain, search, page, perPage, callback) {