DBBase.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  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 <memory>
  19. #include <mutex>
  20. #include <stdexcept>
  21. #include "DBBase.h"
  22. #include <kopano/ECDefs.h>
  23. #include <kopano/EMSAbTag.h>
  24. #include <kopano/stringutil.h>
  25. #include <mapidefs.h>
  26. #include "ECServerEntrypoint.h"
  27. DBPlugin::DBPlugin(std::mutex &pluginlock, ECPluginSharedData *shareddata) :
  28. UserPlugin(pluginlock, shareddata)
  29. {
  30. }
  31. //DBPlugin::~DBPlugin() {
  32. // // Do not delete m_lpDatabase as it is freed when the thread exits
  33. //}
  34. void DBPlugin::InitPlugin() {
  35. if(GetDatabaseObject(&m_lpDatabase) != erSuccess)
  36. throw runtime_error(string("db_init: cannot get handle to database"));
  37. }
  38. std::unique_ptr<signatures_t>
  39. DBPlugin::getAllObjects(const objectid_t &company, objectclass_t objclass)
  40. {
  41. string strQuery =
  42. "SELECT om.externid, om.objectclass, op.value "
  43. "FROM " + (string)DB_OBJECT_TABLE + " AS om "
  44. "LEFT JOIN " + (string)DB_OBJECTPROPERTY_TABLE " AS op "
  45. "ON op.objectid = om.id "
  46. "AND op.propname = '" + OP_MODTIME + "' ";
  47. if (m_bHosted && !company.id.empty()) {
  48. // join company, company itself inclusive
  49. strQuery +=
  50. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS usercompany "
  51. "ON usercompany.objectid = om.id "
  52. "AND ((usercompany.propname = '" + OP_COMPANYID + "' AND usercompany.value = hex('" + m_lpDatabase->Escape(company.id) + "')) OR"
  53. " (usercompany.propname = '" + OP_COMPANYNAME + "' AND om.externid = '" + m_lpDatabase->Escape(company.id) + "'))";
  54. if (objclass != OBJECTCLASS_UNKNOWN)
  55. strQuery += " AND " + OBJECTCLASS_COMPARE_SQL("om.objectclass", objclass);
  56. } else if (objclass != OBJECTCLASS_UNKNOWN)
  57. strQuery += " WHERE " + OBJECTCLASS_COMPARE_SQL("om.objectclass", objclass);
  58. return CreateSignatureList(strQuery);
  59. }
  60. std::unique_ptr<objectdetails_t>
  61. DBPlugin::getObjectDetails(const objectid_t &objectid)
  62. {
  63. std::unique_ptr<std::map<objectid_t, objectdetails_t> > objectdetails;
  64. list<objectid_t> objectids;
  65. objectids.push_back(objectid);
  66. objectdetails = DBPlugin::getObjectDetails(objectids);
  67. if (objectdetails->size() != 1)
  68. throw objectnotfound(objectid.id);
  69. return std::unique_ptr<objectdetails_t>(new objectdetails_t(objectdetails->begin()->second));
  70. }
  71. std::unique_ptr<std::map<objectid_t, objectdetails_t> >
  72. DBPlugin::getObjectDetails(const std::list<objectid_t> &objectids)
  73. {
  74. auto mapdetails = new std::map<objectid_t, objectdetails_t>;
  75. ECRESULT er;
  76. map<objectclass_t, string> objectstrings;
  77. string strQuery;
  78. string strSubQuery;
  79. DB_RESULT lpResult;
  80. DB_ROW lpDBRow = NULL;
  81. DB_LENGTHS lpDBLen = NULL;
  82. objectdetails_t details;
  83. objectid_t lastid;
  84. objectid_t curid;
  85. if(objectids.empty())
  86. return std::unique_ptr<std::map<objectid_t, objectdetails_t> >(mapdetails);
  87. LOG_PLUGIN_DEBUG("%s N=%d", __FUNCTION__, (int)objectids.size());
  88. for (const auto &id : objectids) {
  89. if (!objectstrings[id.objclass].empty())
  90. objectstrings[id.objclass] += ", ";
  91. objectstrings[id.objclass] += "'" + m_lpDatabase->Escape(id.id) + "'";
  92. }
  93. /* Create subquery which combines all externids with the matching objectclass */
  94. for (auto iterStrings = objectstrings.cbegin();
  95. iterStrings != objectstrings.cend(); ++iterStrings) {
  96. if (iterStrings != objectstrings.cbegin())
  97. strSubQuery += " OR ";
  98. strSubQuery += "(o.externid IN (" + iterStrings->second + ") "
  99. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", iterStrings->first) + ")";
  100. }
  101. strQuery =
  102. "SELECT o.externid, o.objectclass, op.propname, op.value "
  103. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  104. "LEFT JOIN "+(string)DB_OBJECTPROPERTY_TABLE+" AS op "
  105. "ON op.objectid=o.id "
  106. "WHERE (" + strSubQuery + ") "
  107. "ORDER BY o.externid, o.objectclass";
  108. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  109. if(er != erSuccess)
  110. throw runtime_error(string("db_query: ") + strerror(er));
  111. while((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL)
  112. {
  113. // No way to determine externid
  114. if (lpDBRow[0] == NULL || lpDBRow[1] == NULL)
  115. continue;
  116. lpDBLen = m_lpDatabase->FetchRowLengths(lpResult);
  117. if (lpDBLen == NULL || lpDBLen[0] == 0)
  118. continue;
  119. curid = objectid_t(string(lpDBRow[0], lpDBLen[0]), (objectclass_t)atoi(lpDBRow[1]));
  120. if (lastid != curid && !lastid.id.empty()) {
  121. details.SetClass(lastid.objclass);
  122. addSendAsToDetails(lastid, &details);
  123. (*mapdetails)[lastid] = details;
  124. // clear details for new object
  125. details = objectdetails_t((objectclass_t)atoi(lpDBRow[1]));
  126. /* By default the sysadmin is SYSTEM, will be overwritten later */
  127. if (details.GetClass() == CONTAINER_COMPANY)
  128. details.SetPropObject(OB_PROP_O_SYSADMIN, objectid_t("SYSTEM", ACTIVE_USER));
  129. }
  130. lastid = curid;
  131. // no properties
  132. if(lpDBRow[2] == NULL || lpDBRow[3] == NULL)
  133. continue;
  134. if(strcmp(lpDBRow[2], OP_LOGINNAME) == 0)
  135. details.SetPropString(OB_PROP_S_LOGIN, lpDBRow[3]);
  136. else if(strcmp(lpDBRow[2], OP_FULLNAME) == 0)
  137. details.SetPropString(OB_PROP_S_FULLNAME, lpDBRow[3]);
  138. else if(strcmp(lpDBRow[2], OP_EMAILADDRESS) == 0)
  139. details.SetPropString(OB_PROP_S_EMAIL, lpDBRow[3]);
  140. else if(strcmp(lpDBRow[2], OP_ISADMIN) == 0)
  141. details.SetPropInt(OB_PROP_I_ADMINLEVEL, min(2, atoi(lpDBRow[3])));
  142. else if(strcmp(lpDBRow[2], OP_GROUPNAME) == 0) {
  143. details.SetPropString(OB_PROP_S_LOGIN, lpDBRow[3]);
  144. details.SetPropString(OB_PROP_S_FULLNAME, lpDBRow[3]);
  145. } else if (strcmp(lpDBRow[2], OP_COMPANYNAME) == 0) {
  146. details.SetPropString(OB_PROP_S_LOGIN, lpDBRow[3]);
  147. details.SetPropString(OB_PROP_S_FULLNAME, lpDBRow[3]);
  148. } else if (strcmp(lpDBRow[2], OP_COMPANYID) == 0) {
  149. // unhex lpDBRow[3]
  150. details.SetPropObject(OB_PROP_O_COMPANYID, objectid_t(hex2bin(lpDBRow[3]), CONTAINER_COMPANY));
  151. } else if (strcmp(lpDBRow[2], OP_COMPANYADMIN) == 0) {
  152. details.SetPropString(OB_PROP_O_SYSADMIN, lpDBRow[3]);
  153. } else if (strcmp(lpDBRow[2], OB_AB_HIDDEN) == 0)
  154. details.SetPropString(OB_PROP_B_AB_HIDDEN, lpDBRow[3]);
  155. else if (strncasecmp(lpDBRow[2], "0x", strlen("0x")) == 0) {
  156. unsigned int key = xtoi(lpDBRow[2]);
  157. if (PROP_TYPE(key) == PT_BINARY)
  158. details.SetPropString((property_key_t)key, base64_decode(lpDBRow[3]));
  159. else
  160. details.SetPropString((property_key_t)xtoi(lpDBRow[2]), lpDBRow[3]);
  161. }
  162. }
  163. if(!lastid.id.empty()) {
  164. details.SetClass(lastid.objclass);
  165. addSendAsToDetails(lastid, &details);
  166. (*mapdetails)[lastid] = details;
  167. }
  168. /* Reset lastid */
  169. lastid = objectid_t();
  170. /* We not have all regular properties, but we might need some MV properties as well */
  171. strQuery =
  172. "SELECT op.propname, op.value, o.externid, o.objectclass "
  173. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  174. "JOIN " + (string)DB_OBJECTMVPROPERTY_TABLE + " AS op "
  175. "ON op.objectid=o.id "
  176. "WHERE (" + strSubQuery + ") "
  177. "ORDER BY o.externid, op.orderid";
  178. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  179. if(er != erSuccess)
  180. throw runtime_error(string("db_query: ") + strerror(er));
  181. std::map<objectid_t, objectdetails_t>::iterator iterDetails;
  182. while((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
  183. if(lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL || lpDBRow[3] == NULL)
  184. continue;
  185. lpDBLen = m_lpDatabase->FetchRowLengths(lpResult);
  186. if (lpDBLen == NULL || lpDBLen[2] == 0)
  187. continue;
  188. curid = objectid_t(string(lpDBRow[2], lpDBLen[2]), (objectclass_t)atoi(lpDBRow[3]));
  189. if (lastid != curid) {
  190. iterDetails = mapdetails->find(curid);
  191. if (iterDetails == mapdetails->cend())
  192. continue;
  193. }
  194. lastid = curid;
  195. if (strncasecmp(lpDBRow[0], "0x", strlen("0x")) == 0) {
  196. unsigned int key = xtoi(lpDBRow[0]);
  197. if (PROP_TYPE(key) == PT_BINARY || PROP_TYPE(key) == PT_MV_BINARY)
  198. iterDetails->second.AddPropString((property_key_t)key, base64_decode(lpDBRow[1]));
  199. else
  200. iterDetails->second.AddPropString((property_key_t)key, lpDBRow[1]);
  201. }
  202. }
  203. return std::unique_ptr<std::map<objectid_t, objectdetails_t> >(mapdetails);
  204. }
  205. std::unique_ptr<signatures_t>
  206. DBPlugin::getSubObjectsForObject(userobject_relation_t relation,
  207. const objectid_t &parentobject)
  208. {
  209. string strQuery =
  210. "SELECT o.externid, o.objectclass, modtime.value "
  211. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  212. "JOIN " + (string)DB_OBJECT_RELATION_TABLE + " AS ort "
  213. "ON o.id = ort.objectid "
  214. "JOIN " + (string)DB_OBJECT_TABLE + " AS p "
  215. "ON p.id = ort.parentobjectid "
  216. "LEFT JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS modtime "
  217. "ON modtime.objectid=o.id "
  218. "AND modtime.propname = '" + OP_MODTIME + "' "
  219. "WHERE p.externid = '" + m_lpDatabase->Escape(parentobject.id) + "' "
  220. "AND ort.relationtype = " + stringify(relation) + " ";
  221. "AND " + OBJECTCLASS_COMPARE_SQL("p.objectclass", parentobject.objclass);
  222. LOG_PLUGIN_DEBUG("%s Relation %x", __FUNCTION__, relation);
  223. return CreateSignatureList(strQuery);
  224. }
  225. std::unique_ptr<signatures_t>
  226. DBPlugin::getParentObjectsForObject(userobject_relation_t relation,
  227. const objectid_t &childobject)
  228. {
  229. string strQuery =
  230. "SELECT o.externid, o.objectclass, modtime.value "
  231. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  232. "JOIN " + (string)DB_OBJECT_RELATION_TABLE + " AS ort "
  233. "ON o.id = ort.parentobjectid "
  234. "JOIN " + (string)DB_OBJECT_TABLE + " AS c "
  235. "ON ort.objectid = c.id "
  236. "LEFT JOIN " +(string)DB_OBJECTPROPERTY_TABLE + " AS modtime "
  237. "ON modtime.objectid = o.id "
  238. "AND modtime.propname = '" + OP_MODTIME + "' "
  239. "WHERE c.externid = '" + m_lpDatabase->Escape(childobject.id) + "' "
  240. "AND ort.relationtype = " + stringify(relation) + " "
  241. "AND " + OBJECTCLASS_COMPARE_SQL("c.objectclass", childobject.objclass);
  242. LOG_PLUGIN_DEBUG("%s Relation %x", __FUNCTION__, relation);
  243. return CreateSignatureList(strQuery);
  244. }
  245. struct props {
  246. property_key_t id;
  247. const char *column;
  248. };
  249. void DBPlugin::changeObject(const objectid_t &objectid, const objectdetails_t &details, const std::list<std::string> *lpDeleteProps)
  250. {
  251. ECRESULT er;
  252. string strQuery;
  253. string strSubQuery;
  254. string strDeleteQuery;
  255. bool bFirstOne = true;
  256. bool bFirstDel = true;
  257. string strData;
  258. property_map anonymousProps;
  259. property_mv_map anonymousMVProps;
  260. unsigned int ulOrderId = 0;
  261. struct props sUserValidProps[] = {
  262. { OB_PROP_S_LOGIN, OP_LOGINNAME, },
  263. { OB_PROP_S_PASSWORD, OP_PASSWORD, },
  264. { OB_PROP_S_EMAIL, OP_EMAILADDRESS, },
  265. { OB_PROP_I_ADMINLEVEL, OP_ISADMIN, },
  266. { OB_PROP_S_FULLNAME, OP_FULLNAME, },
  267. { OB_PROP_O_COMPANYID, OP_COMPANYID, },
  268. { OB_PROP_B_AB_HIDDEN, OB_AB_HIDDEN, },
  269. { (property_key_t)0, NULL },
  270. };
  271. struct props sGroupValidProps[] = {
  272. { OB_PROP_S_FULLNAME, OP_GROUPNAME, },
  273. { OB_PROP_O_COMPANYID, OP_COMPANYID, },
  274. { OB_PROP_S_EMAIL, OP_EMAILADDRESS, },
  275. { OB_PROP_B_AB_HIDDEN, OB_AB_HIDDEN, },
  276. { (property_key_t)0, NULL },
  277. };
  278. struct props sCompanyValidProps[] = {
  279. { OB_PROP_S_FULLNAME, OP_COMPANYNAME, },
  280. { OB_PROP_O_SYSADMIN, OP_COMPANYADMIN, },
  281. { (property_key_t)0, NULL },
  282. };
  283. struct props *sValidProps;
  284. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  285. strSubQuery =
  286. "SELECT id FROM " + (string)DB_OBJECT_TABLE + " "
  287. "WHERE externid = '" + m_lpDatabase->Escape(objectid.id) + "' " +
  288. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", objectid.objclass);
  289. if (lpDeleteProps) {
  290. // delete properties
  291. strDeleteQuery =
  292. "DELETE FROM " + (string)DB_OBJECTPROPERTY_TABLE + " "
  293. "WHERE objectid = (" + strSubQuery + ") " +
  294. " AND propname IN (";
  295. bFirstOne = true;
  296. for (const auto &prop : *lpDeleteProps) {
  297. if (!bFirstOne)
  298. strDeleteQuery += ",";
  299. strDeleteQuery += prop;
  300. bFirstOne = false;
  301. }
  302. strDeleteQuery += ")";
  303. er = m_lpDatabase->DoDelete(strDeleteQuery);
  304. if(er != erSuccess)
  305. throw runtime_error(string("db_query: ") + strerror(er));
  306. }
  307. strQuery = "REPLACE INTO " + (string)DB_OBJECTPROPERTY_TABLE + "(objectid, propname, value) VALUES ";
  308. switch (objectid.objclass) {
  309. case ACTIVE_USER:
  310. case NONACTIVE_USER:
  311. case NONACTIVE_ROOM:
  312. case NONACTIVE_EQUIPMENT:
  313. case NONACTIVE_CONTACT:
  314. sValidProps = sUserValidProps;
  315. break;
  316. case DISTLIST_GROUP:
  317. case DISTLIST_SECURITY:
  318. case DISTLIST_DYNAMIC:
  319. sValidProps = sGroupValidProps;
  320. break;
  321. case CONTAINER_COMPANY:
  322. sValidProps = sCompanyValidProps;
  323. break;
  324. case CONTAINER_ADDRESSLIST:
  325. default:
  326. throw runtime_error("Object is wrong type");
  327. }
  328. bFirstOne = true;
  329. unsigned int i = 0;
  330. while (sValidProps[i].column != NULL) {
  331. string propvalue = details.GetPropString(sValidProps[i].id);
  332. if (strcasecmp(sValidProps[i].column, OP_PASSWORD) == 0 &&
  333. !propvalue.empty() &&
  334. /* Password value has special treatment */
  335. CreateMD5Hash(propvalue, &propvalue) != erSuccess)
  336. /* WARNING input and output point to the same data */
  337. throw runtime_error(string("db_changeUser: create md5"));
  338. if (sValidProps[i].id == OB_PROP_O_COMPANYID) {
  339. propvalue = details.GetPropObject(OB_PROP_O_COMPANYID).id;
  340. // save id as hex in objectproperty.value
  341. propvalue = bin2hex(propvalue.length(), (const unsigned char*)propvalue.data());
  342. }
  343. if (!propvalue.empty()) {
  344. if (!bFirstOne)
  345. strQuery += ",";
  346. strQuery += "((" + strSubQuery + "),'" + m_lpDatabase->Escape(sValidProps[i].column) + "','" + m_lpDatabase->Escape(propvalue) + "')";
  347. bFirstOne = false;
  348. }
  349. ++i;
  350. }
  351. /* Load optional anonymous attributes */
  352. anonymousProps = details.GetPropMapAnonymous();
  353. for (const auto &ap : anonymousProps) {
  354. if (ap.second.empty())
  355. continue;
  356. if (!bFirstOne)
  357. strQuery += ",";
  358. if (PROP_TYPE(ap.first) == PT_BINARY)
  359. strData = base64_encode(reinterpret_cast<const unsigned char *>(ap.second.c_str()), ap.second.size());
  360. else
  361. strData = ap.second;
  362. strQuery +=
  363. "((" + strSubQuery + "),"
  364. "'" + m_lpDatabase->Escape(stringify(ap.first, true)) + "',"
  365. "'" + m_lpDatabase->Escape(strData) + "')";
  366. bFirstOne = false;
  367. }
  368. /* Only update when there were actually properties provided. */
  369. if (!bFirstOne) {
  370. er = m_lpDatabase->DoInsert(strQuery);
  371. if (er != erSuccess)
  372. throw runtime_error(string("db_query: ") + strerror(er));
  373. }
  374. /* Normal properties have been inserted, check for additional MV properties */
  375. bFirstOne = true;
  376. strQuery = "REPLACE INTO " + (string)DB_OBJECTMVPROPERTY_TABLE + "(objectid, propname, orderid, value) VALUES ";
  377. strDeleteQuery =
  378. "DELETE FROM " + (string)DB_OBJECTMVPROPERTY_TABLE + " "
  379. "WHERE objectid = (" + strSubQuery + ") " +
  380. " AND propname IN (";
  381. anonymousMVProps = details.GetPropMapListAnonymous();
  382. for (const auto &mva : anonymousMVProps) {
  383. ulOrderId = 0;
  384. if (!bFirstDel)
  385. strDeleteQuery += ",";
  386. strDeleteQuery += "'" + m_lpDatabase->Escape(stringify(mva.first, true)) + "'";
  387. bFirstDel = false;
  388. if (mva.second.empty())
  389. continue;
  390. for (const auto &prop : mva.second) {
  391. if (!prop.empty()) {
  392. if (!bFirstOne)
  393. strQuery += ",";
  394. if (PROP_TYPE(mva.first) == PT_MV_BINARY)
  395. strData = base64_encode(reinterpret_cast<const unsigned char *>(prop.c_str()), prop.size());
  396. else
  397. strData = prop;
  398. strQuery +=
  399. "((" + strSubQuery + "),"
  400. "'" + m_lpDatabase->Escape(stringify(mva.first, true)) + "',"
  401. "" + stringify(ulOrderId) + ","
  402. "'" + m_lpDatabase->Escape(strData) + "')";
  403. ++ulOrderId;
  404. bFirstOne = false;
  405. }
  406. }
  407. }
  408. strDeleteQuery += ")";
  409. /* Only update when there were actually properties provided. */
  410. if (!bFirstDel) {
  411. /* Make sure all MV properties which are being overriden are being deleted first */
  412. er = m_lpDatabase->DoDelete(strDeleteQuery);
  413. if (er != erSuccess)
  414. throw runtime_error(string("db_query: ") + strerror(er));
  415. }
  416. if (!bFirstOne) {
  417. er = m_lpDatabase->DoInsert(strQuery);
  418. if (er != erSuccess)
  419. throw runtime_error(string("db_query: ") + strerror(er));
  420. }
  421. // Remember modtime for this object
  422. strQuery = "REPLACE INTO " + (string)DB_OBJECTPROPERTY_TABLE + "(objectid, propname, value) VALUES ((" + strSubQuery + "),'" + OP_MODTIME + "', FROM_UNIXTIME("+stringify(time(NULL))+"))";
  423. er = m_lpDatabase->DoInsert(strQuery);
  424. if (er != erSuccess)
  425. throw runtime_error(string("db_query: ") + strerror(er));
  426. // Maybe change user type from active to something nonactive
  427. if (objectid.objclass != details.GetClass() && OBJECTCLASS_TYPE(objectid.objclass) == OBJECTCLASS_TYPE(details.GetClass())) {
  428. strQuery = "UPDATE object SET objectclass = " + stringify(details.GetClass()) +
  429. " WHERE externid = '" + m_lpDatabase->Escape(objectid.id) + "' AND objectclass = " + stringify(objectid.objclass);
  430. er = m_lpDatabase->DoUpdate(strQuery);
  431. if (er != erSuccess)
  432. throw runtime_error(string("db_query: ") + strerror(er));
  433. }
  434. }
  435. objectsignature_t DBPlugin::createObject(const objectdetails_t &details)
  436. {
  437. objectid_t objectid;
  438. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  439. objectid = details.GetPropObject(OB_PROP_O_EXTERNID);
  440. if (!objectid.id.empty()) {
  441. // Offline "force" create object
  442. CreateObjectWithExternId(objectid, details);
  443. } else {
  444. // kopano-admin online create object
  445. objectid = CreateObject(details);
  446. }
  447. // Insert all properties into the database
  448. changeObject(objectid, details, NULL);
  449. // signature is empty on first create. This is OK because it doesn't matter what's in it, as long as it changes when the object is modified
  450. return objectsignature_t(objectid, string());
  451. }
  452. void DBPlugin::deleteObject(const objectid_t &objectid)
  453. {
  454. ECRESULT er;
  455. string strQuery;
  456. string strSubQuery;
  457. DB_RESULT lpResult;
  458. DB_ROW lpDBRow = NULL;
  459. unsigned int ulAffRows = 0;
  460. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  461. strSubQuery =
  462. "SELECT id FROM " + (string)DB_OBJECT_TABLE + " "
  463. "WHERE externid = '" + m_lpDatabase->Escape(objectid.id) + "' "
  464. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", objectid.objclass);
  465. /* First delete company children */
  466. if (objectid.objclass == CONTAINER_COMPANY) {
  467. strQuery = "SELECT objectid FROM " + (string)DB_OBJECTPROPERTY_TABLE +
  468. " WHERE propname = '" + OP_COMPANYID + "' AND value = hex('" + m_lpDatabase->Escape(objectid.id) + "')";
  469. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  470. if (er != erSuccess)
  471. throw runtime_error(string("db_query: ") + strerror(er));
  472. string children;
  473. while ((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
  474. if(lpDBRow[0] == NULL)
  475. throw runtime_error(string("db_row_failed: object null"));
  476. if (!children.empty())
  477. children += ",";
  478. children += lpDBRow[0];
  479. }
  480. if (!children.empty()) {
  481. // remove relations for deleted objects
  482. strQuery =
  483. "DELETE FROM " + (string)DB_OBJECT_RELATION_TABLE + " "
  484. "WHERE objectid IN (" + children + ")";
  485. er = m_lpDatabase->DoDelete(strQuery);
  486. if (er != erSuccess){
  487. //ignore error
  488. }
  489. strQuery =
  490. "DELETE FROM " + (string)DB_OBJECT_RELATION_TABLE + " "
  491. "WHERE parentobjectid IN (" + children + ")";
  492. er = m_lpDatabase->DoDelete(strQuery);
  493. if (er != erSuccess){
  494. //ignore error
  495. }
  496. // delete object properties
  497. strQuery =
  498. "DELETE FROM " + (string)DB_OBJECTPROPERTY_TABLE + " "
  499. "WHERE objectid IN (" + children + ")";
  500. er = m_lpDatabase->DoDelete(strQuery);
  501. if (er != erSuccess){
  502. //ignore error
  503. }
  504. // delete objects themselves
  505. strQuery =
  506. "DELETE FROM " + (string)DB_OBJECT_TABLE + " "
  507. "WHERE id IN (" + children + ")";
  508. er = m_lpDatabase->DoDelete(strQuery);
  509. if (er != erSuccess){
  510. //ignore error
  511. }
  512. }
  513. }
  514. // first delete details of user, since we need the id from the sub query, which is removed next
  515. strQuery = "DELETE FROM "+(string)DB_OBJECTPROPERTY_TABLE+" WHERE objectid=("+strSubQuery+")";
  516. er = m_lpDatabase->DoDelete(strQuery);
  517. if (er != erSuccess){
  518. // ignore error
  519. }
  520. // delete user from object table .. we now have no reference to the user anymore.
  521. strQuery =
  522. "DELETE FROM " + (string)DB_OBJECT_TABLE + " "
  523. "WHERE externid = '" + m_lpDatabase->Escape(objectid.id) + "' "
  524. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", objectid.objclass);
  525. er = m_lpDatabase->DoDelete(strQuery, &ulAffRows);
  526. if(er != erSuccess){
  527. //FIXME: ....
  528. }
  529. if (ulAffRows != 1)
  530. throw objectnotfound("db_user: " + objectid.id);
  531. }
  532. void DBPlugin::addSubObjectRelation(userobject_relation_t relation, const objectid_t &parentobject, const objectid_t &childobject)
  533. {
  534. ECRESULT er = erSuccess;
  535. DB_RESULT lpResult;
  536. string strQuery;
  537. string strParentSubQuery;
  538. string strChildSubQuery;
  539. if (relation == OBJECTRELATION_USER_SENDAS && childobject.objclass != ACTIVE_USER && OBJECTCLASS_TYPE(childobject.objclass) != OBJECTTYPE_DISTLIST)
  540. throw notsupported("only active users can send mail");
  541. LOG_PLUGIN_DEBUG("%s Relation %x", __FUNCTION__, relation);
  542. strParentSubQuery =
  543. "SELECT id FROM " + (string)DB_OBJECT_TABLE + " "
  544. "WHERE externid = '" + m_lpDatabase->Escape(parentobject.id) + "' "
  545. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", parentobject.objclass);
  546. strChildSubQuery =
  547. "SELECT id FROM " + (string)DB_OBJECT_TABLE + " "
  548. "WHERE externid = '" + m_lpDatabase->Escape(childobject.id) + "'"
  549. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", childobject.objclass);
  550. /* Check if relation already exists */
  551. strQuery =
  552. "SELECT objectid FROM " + (string)DB_OBJECT_RELATION_TABLE + " "
  553. "WHERE objectid = (" + strChildSubQuery + ") "
  554. "AND parentobjectid = (" + strParentSubQuery + ") "
  555. "AND relationtype = " + stringify(relation);
  556. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  557. if (er != erSuccess)
  558. throw runtime_error(string("db_query: ") + strerror(er));
  559. if (m_lpDatabase->GetNumRows(lpResult) != 0)
  560. throw collision_error(string("Relation exist: ") + stringify(relation));
  561. /* Insert new relation */
  562. strQuery =
  563. "INSERT INTO " + (string)DB_OBJECT_RELATION_TABLE + " (objectid, parentobjectid, relationtype) "
  564. "VALUES ((" + strChildSubQuery + "),(" + strParentSubQuery + ")," + stringify(relation) + ")";
  565. er = m_lpDatabase->DoInsert(strQuery);
  566. if (er != erSuccess)
  567. throw runtime_error(string("db_query: ") + strerror(er));
  568. }
  569. void DBPlugin::deleteSubObjectRelation(userobject_relation_t relation, const objectid_t &parentobject, const objectid_t &childobject)
  570. {
  571. ECRESULT er = erSuccess;
  572. string strQuery;
  573. unsigned int ulAffRows = 0;
  574. string strParentSubQuery;
  575. string strChildSubQuery;
  576. LOG_PLUGIN_DEBUG("%s Relation %x", __FUNCTION__, relation);
  577. strParentSubQuery =
  578. "SELECT id FROM " + (string)DB_OBJECT_TABLE + " "
  579. "WHERE externid = '" + m_lpDatabase->Escape(parentobject.id) + "' "
  580. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", parentobject.objclass);
  581. strChildSubQuery =
  582. "SELECT id FROM " + (string)DB_OBJECT_TABLE + " "
  583. "WHERE externid = '" + m_lpDatabase->Escape(childobject.id) + "'"
  584. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", childobject.objclass);
  585. strQuery =
  586. "DELETE FROM " + (string)DB_OBJECT_RELATION_TABLE + " "
  587. "WHERE objectid = (" + strChildSubQuery + ") "
  588. "AND parentobjectid = (" + strParentSubQuery + ") "
  589. "AND relationtype = " + stringify(relation);
  590. er = m_lpDatabase->DoDelete(strQuery, &ulAffRows);
  591. if (er != erSuccess)
  592. throw runtime_error("db_query: " + string(strerror(er)));
  593. if (ulAffRows != 1)
  594. throw objectnotfound("db_user: relation " + parentobject.id);
  595. }
  596. std::unique_ptr<signatures_t> DBPlugin::searchObjects(const std::string &match,
  597. const char **search_props, const char *return_prop, unsigned int ulFlags)
  598. {
  599. objectid_t objectid;
  600. std::unique_ptr<signatures_t> lpSignatures(new signatures_t());
  601. string strQuery =
  602. "SELECT DISTINCT ";
  603. if (return_prop)
  604. strQuery += "opret.value, o.objectclass, modtime.value ";
  605. else
  606. strQuery += "o.externid, o.objectclass, modtime.value ";
  607. strQuery +=
  608. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  609. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS op "
  610. "ON op.objectid=o.id ";
  611. if (return_prop) {
  612. strQuery +=
  613. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS opret "
  614. "ON opret.objectid=o.id ";
  615. }
  616. strQuery +=
  617. "LEFT JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS modtime "
  618. "ON modtime.objectid=o.id "
  619. "AND modtime.propname = '" + OP_MODTIME + "' "
  620. "WHERE (";
  621. string strMatch = m_lpDatabase->Escape(match);
  622. string strMatchPrefix;
  623. if (!(ulFlags & EMS_AB_ADDRESS_LOOKUP)) {
  624. strMatch = "%" + strMatch + "%";
  625. strMatchPrefix = " LIKE ";
  626. } else {
  627. strMatchPrefix = " = ";
  628. }
  629. for (unsigned int i = 0; search_props[i] != NULL; ++i) {
  630. strQuery += "(op.propname='" + (string)search_props[i] + "' AND op.value " + strMatchPrefix + " '" + strMatch + "')";
  631. if (search_props[i + 1] != NULL)
  632. strQuery += " OR ";
  633. }
  634. strQuery += ")";
  635. /*
  636. * TODO: check with a point system,
  637. * if you have 2 objects, one have a match of 99% and one 50%
  638. * use the one with 99%
  639. */
  640. lpSignatures = CreateSignatureList(strQuery);
  641. if (lpSignatures->empty())
  642. throw objectnotfound("db_user: no match: " + match);
  643. return lpSignatures;
  644. }
  645. std::unique_ptr<quotadetails_t> DBPlugin::getQuota(const objectid_t &objectid,
  646. bool bGetUserDefault)
  647. {
  648. std::unique_ptr<quotadetails_t> lpDetails;
  649. ECRESULT er = erSuccess;
  650. string strQuery;
  651. DB_RESULT lpResult;
  652. DB_ROW lpDBRow = NULL;
  653. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  654. strQuery =
  655. "SELECT op.propname, op.value "
  656. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  657. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS op "
  658. "ON op.objectid = o.id "
  659. "WHERE o.externid = '" + m_lpDatabase->Escape(objectid.id) + "' "
  660. "AND " + OBJECTCLASS_COMPARE_SQL("o.objectclass", objectid.objclass);
  661. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  662. if (er != erSuccess)
  663. throw runtime_error(string("db_query: ") + strerror(er));
  664. lpDetails.reset(new quotadetails_t());
  665. lpDetails->bIsUserDefaultQuota = bGetUserDefault;
  666. while ((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
  667. if(lpDBRow[0] == NULL || lpDBRow[1] == NULL)
  668. continue;
  669. if (bGetUserDefault) {
  670. if (objectid.objclass != CONTAINER_COMPANY && strcmp(lpDBRow[0], OP_UD_HARDQUOTA) == 0)
  671. lpDetails->llHardSize = atoll(lpDBRow[1]);
  672. else if(objectid.objclass != CONTAINER_COMPANY && strcmp(lpDBRow[0], OP_UD_SOFTQUOTA) == 0)
  673. lpDetails->llSoftSize = atoll(lpDBRow[1]);
  674. else if(strcmp(lpDBRow[0], OP_UD_WARNQUOTA) == 0)
  675. lpDetails->llWarnSize = atoll(lpDBRow[1]);
  676. else if(strcmp(lpDBRow[0], OP_UD_USEDEFAULTQUOTA) == 0)
  677. lpDetails->bUseDefaultQuota = !!atoi(lpDBRow[1]);
  678. } else {
  679. if (objectid.objclass != CONTAINER_COMPANY && strcmp(lpDBRow[0], OP_HARDQUOTA) == 0)
  680. lpDetails->llHardSize = atoll(lpDBRow[1]);
  681. else if(objectid.objclass != CONTAINER_COMPANY && strcmp(lpDBRow[0], OP_SOFTQUOTA) == 0)
  682. lpDetails->llSoftSize = atoll(lpDBRow[1]);
  683. else if(strcmp(lpDBRow[0], OP_WARNQUOTA) == 0)
  684. lpDetails->llWarnSize = atoll(lpDBRow[1]);
  685. else if(strcmp(lpDBRow[0], OP_USEDEFAULTQUOTA) == 0)
  686. lpDetails->bUseDefaultQuota = !!atoi(lpDBRow[1]);
  687. }
  688. }
  689. return lpDetails;
  690. }
  691. void DBPlugin::setQuota(const objectid_t &objectid, const quotadetails_t &quotadetails)
  692. {
  693. ECRESULT er = erSuccess;
  694. string strQuery;
  695. string strSubQuery;
  696. string op_default;
  697. string op_hard;
  698. string op_soft;
  699. string op_warn;
  700. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  701. if (quotadetails.bIsUserDefaultQuota) {
  702. op_default = OP_UD_USEDEFAULTQUOTA;
  703. op_hard = OP_UD_HARDQUOTA;
  704. op_soft = OP_UD_SOFTQUOTA;
  705. op_warn = OP_UD_WARNQUOTA;
  706. } else {
  707. op_default = OP_USEDEFAULTQUOTA;
  708. op_hard = OP_HARDQUOTA;
  709. op_soft = OP_SOFTQUOTA;
  710. op_warn = OP_WARNQUOTA;
  711. }
  712. strSubQuery =
  713. "SELECT id FROM " + (string)DB_OBJECT_TABLE + " "
  714. "WHERE externid = '" + m_lpDatabase->Escape(objectid.id) + "' "
  715. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", objectid.objclass);
  716. // Update new quota settings
  717. strQuery =
  718. "REPLACE INTO " + (string)DB_OBJECTPROPERTY_TABLE + "(objectid, propname, value) VALUES"
  719. "((" + strSubQuery + "), '" + op_default + "','" + stringify(quotadetails.bUseDefaultQuota) + "'),"
  720. "((" + strSubQuery + "), '" + op_hard + "','" + stringify_int64(quotadetails.llHardSize) + "'),"
  721. "((" + strSubQuery + "), '" + op_soft + "','" + stringify_int64(quotadetails.llSoftSize) + "'),"
  722. "((" + strSubQuery + "), '" + op_warn + "','" + stringify_int64(quotadetails.llWarnSize) + "')";
  723. er = m_lpDatabase->DoInsert(strQuery);
  724. if (er != erSuccess) // Maybe on this point the user is broken.
  725. throw runtime_error(string("db_query: ") + strerror(er));
  726. }
  727. std::unique_ptr<signatures_t>
  728. DBPlugin::CreateSignatureList(const std::string &query)
  729. {
  730. ECRESULT er = erSuccess;
  731. std::unique_ptr<signatures_t> objectlist(new signatures_t());
  732. DB_RESULT lpResult;
  733. DB_ROW lpDBRow = NULL;
  734. DB_LENGTHS lpDBLen = NULL;
  735. string signature;
  736. objectclass_t objclass;
  737. objectid_t objectid;
  738. er = m_lpDatabase->DoSelect(query, &lpResult);
  739. if (er != erSuccess)
  740. throw runtime_error(string("db_query: ") + strerror(er));
  741. while ((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
  742. if(lpDBRow[0] == NULL || lpDBRow[1] == NULL)
  743. continue;
  744. if (lpDBRow[2] != NULL)
  745. signature = lpDBRow[2];
  746. objclass = objectclass_t(atoi(lpDBRow[1]));
  747. lpDBLen = m_lpDatabase->FetchRowLengths(lpResult);
  748. assert(lpDBLen != NULL);
  749. if (lpDBLen[0] == 0)
  750. throw runtime_error(string("db_row_failed: object empty"));
  751. objectid = objectid_t(string(lpDBRow[0], lpDBLen[0]), objclass);
  752. objectlist->push_back(objectsignature_t(objectid, signature));
  753. }
  754. return objectlist;
  755. }
  756. ECRESULT DBPlugin::CreateMD5Hash(const std::string &strData, std::string* lpstrResult)
  757. {
  758. MD5_CTX crypt;
  759. std::string salt;
  760. std::ostringstream s;
  761. if (strData.empty() || lpstrResult == NULL)
  762. return KCERR_INVALID_PARAMETER;
  763. s.setf(ios::hex, ios::basefield);
  764. s.fill('0');
  765. s.width(8);
  766. s << rand_mt();
  767. salt = s.str();
  768. MD5_Init(&crypt);
  769. MD5_Update(&crypt, salt.c_str(), salt.size());
  770. MD5_Update(&crypt, strData.c_str(), strData.size());
  771. *lpstrResult = salt + zcp_md5_final_hex(&crypt);
  772. return erSuccess;
  773. }
  774. void DBPlugin::addSendAsToDetails(const objectid_t &objectid, objectdetails_t *lpDetails)
  775. {
  776. std::unique_ptr<signatures_t> sendas = getSubObjectsForObject(OBJECTRELATION_USER_SENDAS, objectid);
  777. for (const auto &objlist : *sendas)
  778. lpDetails->AddPropObject(OB_PROP_LO_SENDAS, objlist.id);
  779. }
  780. std::unique_ptr<abprops_t> DBPlugin::getExtraAddressbookProperties(void)
  781. {
  782. ECRESULT er = erSuccess;
  783. std::unique_ptr<abprops_t> proplist(new abprops_t());
  784. DB_RESULT lpResult;
  785. DB_ROW lpDBRow = NULL;
  786. std::string strQuery;
  787. std::string strTable[2];
  788. LOG_PLUGIN_DEBUG("%s", __FUNCTION__);
  789. strTable[0] = (string)DB_OBJECTPROPERTY_TABLE;
  790. strTable[1] = (string)DB_OBJECTMVPROPERTY_TABLE;
  791. for (unsigned int i = 0; i < 2; ++i) {
  792. strQuery =
  793. "SELECT op.propname "
  794. "FROM " + strTable[i] + " AS op "
  795. "WHERE op.propname LIKE '0x%' "
  796. "OR op.propname LIKE '0X%' "
  797. "GROUP BY op.propname";
  798. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  799. if (er != erSuccess)
  800. throw runtime_error(string("db_query: ") + strerror(er));
  801. while ((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
  802. if(lpDBRow[0] == NULL)
  803. continue;
  804. proplist->push_back(xtoi(lpDBRow[0]));
  805. }
  806. }
  807. return proplist;
  808. }
  809. void DBPlugin::removeAllObjects(objectid_t except)
  810. {
  811. ECRESULT er = erSuccess;
  812. std::string strQuery;
  813. strQuery = "DELETE objectproperty.* FROM objectproperty JOIN object ON object.id = objectproperty.objectid WHERE externid != " + m_lpDatabase->EscapeBinary(except.id);
  814. er = m_lpDatabase->DoDelete(strQuery);
  815. if(er != erSuccess)
  816. throw runtime_error(string("db_query: ") + strerror(er));
  817. strQuery = "DELETE FROM object WHERE externid != " + m_lpDatabase->EscapeBinary(except.id);
  818. er = m_lpDatabase->DoDelete(strQuery);
  819. if(er != erSuccess)
  820. throw runtime_error(string("db_query: ") + strerror(er));
  821. }
  822. void DBPlugin::CreateObjectWithExternId(const objectid_t &objectid, const objectdetails_t &details)
  823. {
  824. ECRESULT er;
  825. string strQuery;
  826. DB_RESULT lpResult;
  827. // check if object already exists
  828. strQuery =
  829. "SELECT id "
  830. "FROM " + (string)DB_OBJECT_TABLE + " "
  831. "WHERE externid = " + m_lpDatabase->EscapeBinary((unsigned char*)objectid.id.c_str(), objectid.id.length()) + " "
  832. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", OBJECTCLASS_CLASSTYPE(details.GetClass()));
  833. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  834. if (er != erSuccess)
  835. throw runtime_error(string("db_query: ") + strerror(er));
  836. if (m_lpDatabase->FetchRow(lpResult) != NULL)
  837. throw collision_error(string("Object exists: ") + bin2hex(objectid.id));
  838. strQuery =
  839. "INSERT INTO " + (string)DB_OBJECT_TABLE + "(externid, objectclass) "
  840. "VALUES('" + m_lpDatabase->Escape(objectid.id) + "'," + stringify(objectid.objclass) + ")";
  841. er = m_lpDatabase->DoInsert(strQuery);
  842. if (er != erSuccess)
  843. throw runtime_error(string("db_query: ") + strerror(er));
  844. }
  845. objectid_t DBPlugin::CreateObject(const objectdetails_t &details)
  846. {
  847. ECRESULT er;
  848. string strQuery;
  849. DB_RESULT lpResult;
  850. DB_ROW lpDBRow = NULL;
  851. string strPropName;
  852. string strPropValue;
  853. GUID guidExternId = {0};
  854. string strExternId;
  855. switch (details.GetClass()) {
  856. case ACTIVE_USER:
  857. case NONACTIVE_USER:
  858. case NONACTIVE_ROOM:
  859. case NONACTIVE_EQUIPMENT:
  860. case NONACTIVE_CONTACT:
  861. strPropName = OP_LOGINNAME;
  862. strPropValue = details.GetPropString(OB_PROP_S_LOGIN);
  863. break;
  864. case DISTLIST_GROUP:
  865. case DISTLIST_SECURITY:
  866. case DISTLIST_DYNAMIC:
  867. strPropName = OP_GROUPNAME;
  868. strPropValue = details.GetPropString(OB_PROP_S_FULLNAME);
  869. break;
  870. case CONTAINER_COMPANY:
  871. strPropName = OP_COMPANYNAME;
  872. strPropValue = details.GetPropString(OB_PROP_S_FULLNAME);
  873. break;
  874. case CONTAINER_ADDRESSLIST:
  875. default:
  876. throw runtime_error("Object is wrong type");
  877. }
  878. // check if object already exists
  879. strQuery =
  880. "SELECT o.id, op.value "
  881. "FROM " + (string)DB_OBJECT_TABLE + " AS o "
  882. "JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS op "
  883. "ON op.objectid = o.id AND op.propname = '" + strPropName + "' "
  884. "LEFT JOIN " + (string)DB_OBJECTPROPERTY_TABLE + " AS oc "
  885. "ON oc.objectid = o.id AND oc.propname = '" + (string)OP_COMPANYID + "' "
  886. "WHERE op.value = '" + m_lpDatabase->Escape(strPropValue) + "' "
  887. "AND " + OBJECTCLASS_COMPARE_SQL("o.objectclass", OBJECTCLASS_CLASSTYPE(details.GetClass()));
  888. if (m_bHosted && details.GetClass() != CONTAINER_COMPANY)
  889. strQuery += " AND (oc.value IS NULL OR oc.value = hex('" + m_lpDatabase->Escape(details.GetPropObject(OB_PROP_O_COMPANYID).id) + "'))";
  890. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  891. if (er != erSuccess)
  892. throw runtime_error(string("db_query: ") + strerror(er));
  893. while ((lpDBRow = m_lpDatabase->FetchRow(lpResult)) != NULL)
  894. if (lpDBRow[1] != NULL && strcasecmp(lpDBRow[1], strPropValue.c_str()) == 0)
  895. throw collision_error(string("Object exist: ") + strPropValue);
  896. if (CoCreateGuid(&guidExternId) != S_OK)
  897. throw runtime_error("failed to generate extern id");
  898. strExternId.assign((const char*)&guidExternId, sizeof(guidExternId));
  899. strQuery =
  900. "INSERT INTO " + (string)DB_OBJECT_TABLE + "(objectclass, externid) "
  901. "VALUES (" + stringify(details.GetClass()) + "," +
  902. m_lpDatabase->EscapeBinary((unsigned char*)strExternId.c_str(), strExternId.length()) + ")";
  903. er = m_lpDatabase->DoInsert(strQuery);
  904. if (er != erSuccess)
  905. throw runtime_error(string("db_query: ") + strerror(er));
  906. return objectid_t(strExternId, details.GetClass());
  907. }