123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program 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
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <kopano/platform.h>
- #include <iostream>
- #include <string>
- #include <stdexcept>
- #include <algorithm>
- #include <cerrno>
- #include <cassert>
- #include <mutex>
- #include <kopano/EMSAbTag.h>
- #include <kopano/ECConfig.h>
- #include <kopano/ECDefs.h>
- #include <kopano/ECLogger.h>
- #include <kopano/ECPluginSharedData.h>
- #include <kopano/stringutil.h>
- using namespace std;
- #include "ECDatabaseFactory.h"
- #include "DBUserPlugin.h"
- #include <kopano/ecversion.h>
- extern "C" {
- UserPlugin *getUserPluginInstance(std::mutex &pluginlock,
- ECPluginSharedData *shareddata)
- {
- return new DBUserPlugin(pluginlock, shareddata);
- }
- void deleteUserPluginInstance(UserPlugin *up) {
- delete up;
- }
- int getUserPluginVersion() {
- return PROJECT_VERSION_REVISION;
- }
- }
- DBUserPlugin::DBUserPlugin(std::mutex &pluginlock,
- ECPluginSharedData *shareddata) :
- DBPlugin(pluginlock, shareddata)
- {
- if (m_bDistributed)
- throw notsupported("Distributed Kopano not supported when using the Database Plugin");
- }
- void DBUserPlugin::InitPlugin()
- {
- DBPlugin::InitPlugin();
- }
- objectsignature_t DBUserPlugin::resolveName(objectclass_t objclass, const string &name, const objectid_t &company)
- {
- objectid_t id;
- ECRESULT er;
- string strQuery;
- DB_RESULT lpResult;
- DB_ROW lpDBRow = NULL;
- DB_LENGTHS lpDBLen = NULL;
- string signature;
- const char *lpszSearchProperty;
- if (company.id.empty()) {
- LOG_PLUGIN_DEBUG("%s Class %x, Name %s", __FUNCTION__, objclass, name.c_str());
- } else {
- LOG_PLUGIN_DEBUG("%s Class %x, Name %s, Company %s", __FUNCTION__, objclass, name.c_str(), company.id.c_str());
- }
- switch (objclass) {
- case OBJECTCLASS_USER:
- case ACTIVE_USER:
- case NONACTIVE_USER:
- case NONACTIVE_ROOM:
- case NONACTIVE_EQUIPMENT:
- case NONACTIVE_CONTACT:
- lpszSearchProperty = OP_LOGINNAME;
- break;
- case OBJECTCLASS_DISTLIST:
- case DISTLIST_GROUP:
- case DISTLIST_SECURITY:
- case DISTLIST_DYNAMIC:
- lpszSearchProperty = OP_GROUPNAME;
- break;
- case CONTAINER_COMPANY:
- lpszSearchProperty = OP_COMPANYNAME;
- break;
- case OBJECTCLASS_CONTAINER:
- case CONTAINER_ADDRESSLIST:
- case OBJECTCLASS_UNKNOWN:
- // We don't have a search property, search through all fields
- lpszSearchProperty = NULL;
- break;
- default:
- throw runtime_error("Object is wrong type");
- }
- /*
- * Join the DB_OBJECT_TABLE together twice. This is done because
- * we need to link the entry for the user and the entry for the
- * company into a single line. Once those are all linked we can
- * set the WHERE restrictions to get the correct line.
- */
- strQuery =
- "SELECT DISTINCT o.externid, o.objectclass, modtime.value, user.value "
- "FROM " + (string)DB_OBJECT_TABLE + " AS o "
- "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS user "
- "ON user.objectid = o.id "
- "AND upper(user.value) = upper('" + m_lpDatabase->Escape(name) + "') ";
- if (lpszSearchProperty)
- strQuery += "AND user.propname = '" + (string)lpszSearchProperty + "' ";
- if (m_bHosted && !company.id.empty()) {
- // join company, company itself inclusive
- strQuery +=
- "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS usercompany "
- "ON usercompany.objectid = o.id "
- "AND (usercompany.propname = '" + OP_COMPANYID + "' AND usercompany.value = hex('" + m_lpDatabase->Escape(company.id) + "') OR "
- "usercompany.propname = '" + OP_COMPANYNAME + "' AND usercompany.objectid = '" + m_lpDatabase->Escape(company.id) + "')";
- }
- strQuery +=
- "LEFT JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS modtime "
- "ON modtime.propname = '" + OP_MODTIME + "' "
- "AND modtime.objectid = o.id ";
- if (objclass != OBJECTCLASS_UNKNOWN)
- strQuery += "WHERE " + OBJECTCLASS_COMPARE_SQL("o.objectclass", objclass);
- er = m_lpDatabase->DoSelect(strQuery, &lpResult);
- if(er != erSuccess) {
- throw runtime_error(string("db_query: ") + strerror(er));
- }
- while ((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
- if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[3] == NULL)
- throw runtime_error(string("db_row_failed: object null"));
- if (strcasecmp(lpDBRow[3], name.c_str()) != 0)
- continue;
- lpDBLen = m_lpDatabase->FetchRowLengths(lpResult);
- if (lpDBLen == NULL || lpDBLen[0] == 0)
- throw runtime_error(string("db_row_failed: object empty"));
- if(lpDBRow[2] != NULL)
- signature = lpDBRow[2];
- id = objectid_t(string(lpDBRow[0], lpDBLen[0]), (objectclass_t)atoi(lpDBRow[1]));
- return objectsignature_t(id, signature);
- }
- throw objectnotfound(name);
- }
- objectsignature_t DBUserPlugin::authenticateUser(const string &username, const string &password, const objectid_t &company)
- {
- objectid_t objectid;
- std::string signature;
- ECRESULT er;
- string strQuery;
- DB_RESULT lpResult;
- DB_ROW lpDBRow = NULL;
- DB_LENGTHS lpDBLen = NULL;
- std::string salt;
- std::string strMD5;
- /*
- * Join the DB_OBJECT_TABLE together twice. This is done because
- * we need to link the entry for the user and the entry for the
- * company into a single line. Once those are all linked we can
- * set the WHERE restrictions to get the correct line.
- */
- strQuery =
- "SELECT pass.propname, pass.value, o.externid, modtime.value, op.value "
- "FROM " + (string)DB_OBJECT_TABLE + " AS o "
- "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS op "
- "ON o.id = op.objectid "
- "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS pass "
- "ON pass.objectid = o.id ";
- if (m_bHosted && !company.id.empty()) {
- // join company, company itself inclusive
- strQuery +=
- "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS usercompany "
- "ON usercompany.objectid = o.id "
- "AND (usercompany.propname = '" + OP_COMPANYID + "' AND usercompany.value = hex('" + m_lpDatabase->Escape(company.id) + "') OR "
- "usercompany.propname = '" + OP_COMPANYNAME + "' AND usercompany.objectid = '" + m_lpDatabase->Escape(company.id) + "')";
- }
- strQuery +=
- "LEFT JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS modtime "
- "ON modtime.objectid = o.id "
- "AND modtime.propname = '" + OP_MODTIME + "' "
- "WHERE " + OBJECTCLASS_COMPARE_SQL("o.objectclass", ACTIVE_USER) + " "
- "AND op.propname = '" + (string)OP_LOGINNAME + "' "
- "AND op.value = '" + m_lpDatabase->Escape(username) + "' "
- "AND pass.propname = '" + (string)OP_PASSWORD "'";
- er = m_lpDatabase->DoSelect(strQuery, &lpResult);
- if(er != erSuccess) {
- throw runtime_error(string("db_query: ") + strerror(er));
- }
- while ((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
- if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL || lpDBRow[4] == NULL)
- throw runtime_error("Trying to authenticate failed: database error");
- if (strcasecmp(lpDBRow[4], username.c_str()) != 0)
- continue;
- lpDBLen = m_lpDatabase->FetchRowLengths(lpResult);
- if (lpDBLen == NULL || lpDBLen[2] == 0)
- throw runtime_error("Trying to authenticate failed: database error");
- if(strcmp(lpDBRow[0], OP_PASSWORD) == 0)
- {
- // Check Password
- MD5_CTX crypt;
- salt = lpDBRow[1];
- salt.resize(8);
- MD5_Init(&crypt);
- MD5_Update(&crypt, salt.c_str(), salt.length());
- MD5_Update(&crypt, password.c_str(), password.size());
- strMD5 = salt + zcp_md5_final_hex(&crypt);
- if(strMD5.compare((string)lpDBRow[1]) == 0) {
- objectid = objectid_t(string(lpDBRow[2], lpDBLen[2]), ACTIVE_USER); // Password is oke
- } else {
- throw login_error("Trying to authenticate failed: wrong username or password");
- }
- } else {
- throw login_error("Trying to authenticate failed: wrong username or password");
- }
- if(lpDBRow[3] != NULL)
- signature = lpDBRow[3];
- return objectsignature_t(objectid, signature);
- }
- throw login_error("Trying to authenticate failed: wrong username or password");
- }
- std::unique_ptr<signatures_t>
- DBUserPlugin::searchObject(const std::string &match, unsigned int ulFlags)
- {
- const char *search_props[] =
- {
- OP_LOGINNAME, OP_FULLNAME, OP_EMAILADDRESS, /* This will match all users */
- OP_GROUPNAME, /* This will match all groups */
- OP_COMPANYNAME, /* This will match all companies */
- NULL,
- };
- LOG_PLUGIN_DEBUG("%s %s flags:%x", __FUNCTION__, match.c_str(), ulFlags);
- return searchObjects(match.c_str(), search_props, NULL, ulFlags);
- }
- void DBUserPlugin::modifyObjectId(const objectid_t &oldId, const objectid_t &newId)
- {
- throw notimplemented("Modifying objects is not supported when using the DB user plugin.");
- }
- void DBUserPlugin::setQuota(const objectid_t &objectid, const quotadetails_t "adetails)
- {
- string strQuery;
- ECRESULT er = erSuccess;
- DB_RESULT lpResult;
- DB_ROW lpDBRow = NULL;
- // check if user exist
- strQuery =
- "SELECT o.externid "
- "FROM " + (string)DB_OBJECT_TABLE + " AS o "
- "WHERE o.externid='" + m_lpDatabase->Escape(objectid.id) + "' "
- "AND " + OBJECTCLASS_COMPARE_SQL("o.objectclass", objectid.objclass);
- er = m_lpDatabase->DoSelect(strQuery, &lpResult);
- if(er != erSuccess)
- throw runtime_error(string("db_query: ") + strerror(er));
- if(m_lpDatabase->GetNumRows(lpResult) != 1)
- throw objectnotfound(objectid.id);
- lpDBRow = m_lpDatabase->FetchRow(lpResult);
- if(lpDBRow == NULL || lpDBRow[0] == NULL)
- throw runtime_error(string("db_row_failed: object null"));
- DBPlugin::setQuota(objectid, quotadetails);
- }
- std::unique_ptr<objectdetails_t> DBUserPlugin::getPublicStoreDetails(void)
- {
- throw notsupported("public store details");
- }
- std::unique_ptr<serverdetails_t>
- DBUserPlugin::getServerDetails(const std::string &server)
- {
- throw notsupported("server details");
- }
- std::unique_ptr<serverlist_t> DBUserPlugin::getServers(void)
- {
- throw notsupported("server list");
- }
- void DBUserPlugin::addSubObjectRelation(userobject_relation_t relation, const objectid_t &parentobject, const objectid_t &childobject)
- {
- ECRESULT er = erSuccess;
- string strQuery;
- DB_RESULT lpResult;
- // Check if parent exist
- strQuery =
- "SELECT o.externid "
- "FROM " + (string)DB_OBJECT_TABLE + " AS o "
- "WHERE o.externid='" + m_lpDatabase->Escape(parentobject.id) + "' "
- "AND " + OBJECTCLASS_COMPARE_SQL("o.objectclass", parentobject.objclass);
- er = m_lpDatabase->DoSelect(strQuery, &lpResult);
- if (er != erSuccess)
- throw runtime_error(string("db_query: ") + strerror(er));
- if(m_lpDatabase->GetNumRows(lpResult) != 1)
- throw objectnotfound("db_user: Relation does not exist, id:" + parentobject.id);
- DBPlugin::addSubObjectRelation(relation, parentobject, childobject);
- }
|