DBUserPlugin.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/platform.h>
  18. #include <iostream>
  19. #include <string>
  20. #include <stdexcept>
  21. #include <algorithm>
  22. #include <cerrno>
  23. #include <cassert>
  24. #include <mutex>
  25. #include <kopano/EMSAbTag.h>
  26. #include <kopano/ECConfig.h>
  27. #include <kopano/ECDefs.h>
  28. #include <kopano/ECLogger.h>
  29. #include <kopano/ECPluginSharedData.h>
  30. #include <kopano/stringutil.h>
  31. using namespace std;
  32. #include "ECDatabaseFactory.h"
  33. #include "DBUserPlugin.h"
  34. #include <kopano/ecversion.h>
  35. extern "C" {
  36. UserPlugin *getUserPluginInstance(std::mutex &pluginlock,
  37. ECPluginSharedData *shareddata)
  38. {
  39. return new DBUserPlugin(pluginlock, shareddata);
  40. }
  41. void deleteUserPluginInstance(UserPlugin *up) {
  42. delete up;
  43. }
  44. int getUserPluginVersion() {
  45. return PROJECT_VERSION_REVISION;
  46. }
  47. }
  48. DBUserPlugin::DBUserPlugin(std::mutex &pluginlock,
  49. ECPluginSharedData *shareddata) :
  50. DBPlugin(pluginlock, shareddata)
  51. {
  52. if (m_bDistributed)
  53. throw notsupported("Distributed Kopano not supported when using the Database Plugin");
  54. }
  55. void DBUserPlugin::InitPlugin()
  56. {
  57. DBPlugin::InitPlugin();
  58. }
  59. objectsignature_t DBUserPlugin::resolveName(objectclass_t objclass, const string &name, const objectid_t &company)
  60. {
  61. objectid_t id;
  62. ECRESULT er;
  63. string strQuery;
  64. DB_RESULT lpResult;
  65. DB_ROW lpDBRow = NULL;
  66. DB_LENGTHS lpDBLen = NULL;
  67. string signature;
  68. const char *lpszSearchProperty;
  69. if (company.id.empty()) {
  70. LOG_PLUGIN_DEBUG("%s Class %x, Name %s", __FUNCTION__, objclass, name.c_str());
  71. } else {
  72. LOG_PLUGIN_DEBUG("%s Class %x, Name %s, Company %s", __FUNCTION__, objclass, name.c_str(), company.id.c_str());
  73. }
  74. switch (objclass) {
  75. case OBJECTCLASS_USER:
  76. case ACTIVE_USER:
  77. case NONACTIVE_USER:
  78. case NONACTIVE_ROOM:
  79. case NONACTIVE_EQUIPMENT:
  80. case NONACTIVE_CONTACT:
  81. lpszSearchProperty = OP_LOGINNAME;
  82. break;
  83. case OBJECTCLASS_DISTLIST:
  84. case DISTLIST_GROUP:
  85. case DISTLIST_SECURITY:
  86. case DISTLIST_DYNAMIC:
  87. lpszSearchProperty = OP_GROUPNAME;
  88. break;
  89. case CONTAINER_COMPANY:
  90. lpszSearchProperty = OP_COMPANYNAME;
  91. break;
  92. case OBJECTCLASS_CONTAINER:
  93. case CONTAINER_ADDRESSLIST:
  94. case OBJECTCLASS_UNKNOWN:
  95. // We don't have a search property, search through all fields
  96. lpszSearchProperty = NULL;
  97. break;
  98. default:
  99. throw runtime_error("Object is wrong type");
  100. }
  101. /*
  102. * Join the DB_OBJECT_TABLE together twice. This is done because
  103. * we need to link the entry for the user and the entry for the
  104. * company into a single line. Once those are all linked we can
  105. * set the WHERE restrictions to get the correct line.
  106. */
  107. strQuery =
  108. "SELECT DISTINCT o.externid, o.objectclass, modtime.value, user.value "
  109. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  110. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS user "
  111. "ON user.objectid = o.id "
  112. "AND upper(user.value) = upper('" + m_lpDatabase->Escape(name) + "') ";
  113. if (lpszSearchProperty)
  114. strQuery += "AND user.propname = '" + (string)lpszSearchProperty + "' ";
  115. if (m_bHosted && !company.id.empty()) {
  116. // join company, company itself inclusive
  117. strQuery +=
  118. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS usercompany "
  119. "ON usercompany.objectid = o.id "
  120. "AND (usercompany.propname = '" + OP_COMPANYID + "' AND usercompany.value = hex('" + m_lpDatabase->Escape(company.id) + "') OR "
  121. "usercompany.propname = '" + OP_COMPANYNAME + "' AND usercompany.objectid = '" + m_lpDatabase->Escape(company.id) + "')";
  122. }
  123. strQuery +=
  124. "LEFT JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS modtime "
  125. "ON modtime.propname = '" + OP_MODTIME + "' "
  126. "AND modtime.objectid = o.id ";
  127. if (objclass != OBJECTCLASS_UNKNOWN)
  128. strQuery += "WHERE " + OBJECTCLASS_COMPARE_SQL("o.objectclass", objclass);
  129. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  130. if(er != erSuccess) {
  131. throw runtime_error(string("db_query: ") + strerror(er));
  132. }
  133. while ((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
  134. if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[3] == NULL)
  135. throw runtime_error(string("db_row_failed: object null"));
  136. if (strcasecmp(lpDBRow[3], name.c_str()) != 0)
  137. continue;
  138. lpDBLen = m_lpDatabase->FetchRowLengths(lpResult);
  139. if (lpDBLen == NULL || lpDBLen[0] == 0)
  140. throw runtime_error(string("db_row_failed: object empty"));
  141. if(lpDBRow[2] != NULL)
  142. signature = lpDBRow[2];
  143. id = objectid_t(string(lpDBRow[0], lpDBLen[0]), (objectclass_t)atoi(lpDBRow[1]));
  144. return objectsignature_t(id, signature);
  145. }
  146. throw objectnotfound(name);
  147. }
  148. objectsignature_t DBUserPlugin::authenticateUser(const string &username, const string &password, const objectid_t &company)
  149. {
  150. objectid_t objectid;
  151. std::string signature;
  152. ECRESULT er;
  153. string strQuery;
  154. DB_RESULT lpResult;
  155. DB_ROW lpDBRow = NULL;
  156. DB_LENGTHS lpDBLen = NULL;
  157. std::string salt;
  158. std::string strMD5;
  159. /*
  160. * Join the DB_OBJECT_TABLE together twice. This is done because
  161. * we need to link the entry for the user and the entry for the
  162. * company into a single line. Once those are all linked we can
  163. * set the WHERE restrictions to get the correct line.
  164. */
  165. strQuery =
  166. "SELECT pass.propname, pass.value, o.externid, modtime.value, op.value "
  167. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  168. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS op "
  169. "ON o.id = op.objectid "
  170. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS pass "
  171. "ON pass.objectid = o.id ";
  172. if (m_bHosted && !company.id.empty()) {
  173. // join company, company itself inclusive
  174. strQuery +=
  175. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS usercompany "
  176. "ON usercompany.objectid = o.id "
  177. "AND (usercompany.propname = '" + OP_COMPANYID + "' AND usercompany.value = hex('" + m_lpDatabase->Escape(company.id) + "') OR "
  178. "usercompany.propname = '" + OP_COMPANYNAME + "' AND usercompany.objectid = '" + m_lpDatabase->Escape(company.id) + "')";
  179. }
  180. strQuery +=
  181. "LEFT JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS modtime "
  182. "ON modtime.objectid = o.id "
  183. "AND modtime.propname = '" + OP_MODTIME + "' "
  184. "WHERE " + OBJECTCLASS_COMPARE_SQL("o.objectclass", ACTIVE_USER) + " "
  185. "AND op.propname = '" + (string)OP_LOGINNAME + "' "
  186. "AND op.value = '" + m_lpDatabase->Escape(username) + "' "
  187. "AND pass.propname = '" + (string)OP_PASSWORD "'";
  188. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  189. if(er != erSuccess) {
  190. throw runtime_error(string("db_query: ") + strerror(er));
  191. }
  192. while ((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
  193. if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL || lpDBRow[4] == NULL)
  194. throw runtime_error("Trying to authenticate failed: database error");
  195. if (strcasecmp(lpDBRow[4], username.c_str()) != 0)
  196. continue;
  197. lpDBLen = m_lpDatabase->FetchRowLengths(lpResult);
  198. if (lpDBLen == NULL || lpDBLen[2] == 0)
  199. throw runtime_error("Trying to authenticate failed: database error");
  200. if(strcmp(lpDBRow[0], OP_PASSWORD) == 0)
  201. {
  202. // Check Password
  203. MD5_CTX crypt;
  204. salt = lpDBRow[1];
  205. salt.resize(8);
  206. MD5_Init(&crypt);
  207. MD5_Update(&crypt, salt.c_str(), salt.length());
  208. MD5_Update(&crypt, password.c_str(), password.size());
  209. strMD5 = salt + zcp_md5_final_hex(&crypt);
  210. if(strMD5.compare((string)lpDBRow[1]) == 0) {
  211. objectid = objectid_t(string(lpDBRow[2], lpDBLen[2]), ACTIVE_USER); // Password is oke
  212. } else {
  213. throw login_error("Trying to authenticate failed: wrong username or password");
  214. }
  215. } else {
  216. throw login_error("Trying to authenticate failed: wrong username or password");
  217. }
  218. if(lpDBRow[3] != NULL)
  219. signature = lpDBRow[3];
  220. return objectsignature_t(objectid, signature);
  221. }
  222. throw login_error("Trying to authenticate failed: wrong username or password");
  223. }
  224. std::unique_ptr<signatures_t>
  225. DBUserPlugin::searchObject(const std::string &match, unsigned int ulFlags)
  226. {
  227. const char *search_props[] =
  228. {
  229. OP_LOGINNAME, OP_FULLNAME, OP_EMAILADDRESS, /* This will match all users */
  230. OP_GROUPNAME, /* This will match all groups */
  231. OP_COMPANYNAME, /* This will match all companies */
  232. NULL,
  233. };
  234. LOG_PLUGIN_DEBUG("%s %s flags:%x", __FUNCTION__, match.c_str(), ulFlags);
  235. return searchObjects(match.c_str(), search_props, NULL, ulFlags);
  236. }
  237. void DBUserPlugin::modifyObjectId(const objectid_t &oldId, const objectid_t &newId)
  238. {
  239. throw notimplemented("Modifying objects is not supported when using the DB user plugin.");
  240. }
  241. void DBUserPlugin::setQuota(const objectid_t &objectid, const quotadetails_t &quotadetails)
  242. {
  243. string strQuery;
  244. ECRESULT er = erSuccess;
  245. DB_RESULT lpResult;
  246. DB_ROW lpDBRow = NULL;
  247. // check if user exist
  248. strQuery =
  249. "SELECT o.externid "
  250. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  251. "WHERE o.externid='" + m_lpDatabase->Escape(objectid.id) + "' "
  252. "AND " + OBJECTCLASS_COMPARE_SQL("o.objectclass", objectid.objclass);
  253. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  254. if(er != erSuccess)
  255. throw runtime_error(string("db_query: ") + strerror(er));
  256. if(m_lpDatabase->GetNumRows(lpResult) != 1)
  257. throw objectnotfound(objectid.id);
  258. lpDBRow = m_lpDatabase->FetchRow(lpResult);
  259. if(lpDBRow == NULL || lpDBRow[0] == NULL)
  260. throw runtime_error(string("db_row_failed: object null"));
  261. DBPlugin::setQuota(objectid, quotadetails);
  262. }
  263. std::unique_ptr<objectdetails_t> DBUserPlugin::getPublicStoreDetails(void)
  264. {
  265. throw notsupported("public store details");
  266. }
  267. std::unique_ptr<serverdetails_t>
  268. DBUserPlugin::getServerDetails(const std::string &server)
  269. {
  270. throw notsupported("server details");
  271. }
  272. std::unique_ptr<serverlist_t> DBUserPlugin::getServers(void)
  273. {
  274. throw notsupported("server list");
  275. }
  276. void DBUserPlugin::addSubObjectRelation(userobject_relation_t relation, const objectid_t &parentobject, const objectid_t &childobject)
  277. {
  278. ECRESULT er = erSuccess;
  279. string strQuery;
  280. DB_RESULT lpResult;
  281. // Check if parent exist
  282. strQuery =
  283. "SELECT o.externid "
  284. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  285. "WHERE o.externid='" + m_lpDatabase->Escape(parentobject.id) + "' "
  286. "AND " + OBJECTCLASS_COMPARE_SQL("o.objectclass", parentobject.objclass);
  287. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  288. if (er != erSuccess)
  289. throw runtime_error(string("db_query: ") + strerror(er));
  290. if(m_lpDatabase->GetNumRows(lpResult) != 1)
  291. throw objectnotfound("db_user: Relation does not exist, id:" + parentobject.id);
  292. DBPlugin::addSubObjectRelation(relation, parentobject, childobject);
  293. }