StorageUtil.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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 <kopano/memory.hpp>
  19. #include "StorageUtil.h"
  20. #include "ECDatabase.h"
  21. #include "ECAttachmentStorage.h"
  22. #include "ECSession.h"
  23. #include "ECSessionManager.h"
  24. #include "ECSecurity.h"
  25. #include "cmdutil.hpp"
  26. #include <edkmdb.h>
  27. using namespace KCHL;
  28. namespace KC {
  29. // External objects
  30. extern ECSessionManager *g_lpSessionManager; // ECServerEntrypoint.cpp
  31. ECRESULT CreateAttachmentStorage(ECDatabase *lpDatabase, ECAttachmentStorage **lppAttachmentStorage)
  32. {
  33. return ECAttachmentStorage::CreateAttachmentStorage(lpDatabase, g_lpSessionManager->GetConfig(), lppAttachmentStorage);
  34. }
  35. ECRESULT CreateObject(ECSession *lpecSession, ECDatabase *lpDatabase, unsigned int ulParentObjId, unsigned int ulParentType, unsigned int ulObjType, unsigned int ulFlags, unsigned int *lpulObjId)
  36. {
  37. ECRESULT er;
  38. unsigned int ulNewObjId = 0;
  39. unsigned int ulAffected = 0;
  40. unsigned int ulOwner = 0;
  41. std::string strQuery;
  42. assert(ulParentType == MAPI_FOLDER || ulParentType == MAPI_MESSAGE || ulParentType == MAPI_ATTACH);
  43. //
  44. // We skip quota checking because we do this during writeProps.
  45. if(ulParentType == MAPI_FOLDER) {
  46. // Only check creating items in folders. Creating items in messages and attachments is not security-checked since
  47. // you should check access rights on the top-level message, not on the underlying objects.
  48. er = lpecSession->GetSecurity()->CheckPermission(ulParentObjId, ecSecurityCreate);
  49. if(er != erSuccess)
  50. return er;
  51. }
  52. ulOwner = lpecSession->GetSecurity()->GetUserId(ulParentObjId); // Owner of object is either the current user or the owner of the folder
  53. // Create object
  54. strQuery = "INSERT INTO hierarchy (parent, type, flags, owner) values("+stringify(ulParentObjId)+", "+stringify(ulObjType)+", "+stringify(ulFlags)+", "+stringify(ulOwner)+")";
  55. er = lpDatabase->DoInsert(strQuery, &ulNewObjId, &ulAffected);
  56. if(er != erSuccess)
  57. return er;
  58. if (ulObjType == MAPI_MESSAGE) {
  59. strQuery = "INSERT INTO properties (hierarchyid, tag, type, val_ulong) VALUES ("+ stringify(ulNewObjId) + "," + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + "," + stringify(PROP_TYPE(PR_MESSAGE_FLAGS)) + "," + stringify(ulFlags) + ")";
  60. er = lpDatabase->DoInsert(strQuery);
  61. if (er != erSuccess)
  62. return er;
  63. }
  64. // Save this item in the cache, as there is a very high probability that this data will be required very soon (almost 100% sure)
  65. g_lpSessionManager->GetCacheManager()->SetObject(ulNewObjId, ulParentObjId, ulOwner, ulFlags, ulObjType);
  66. // return new object id to saveObject
  67. if (lpulObjId)
  68. *lpulObjId = ulNewObjId;
  69. return erSuccess;
  70. }
  71. /* Get the size of an object, PR_MESSAGE_SIZE or PR_ATTACH_SIZE */
  72. ECRESULT GetObjectSize(ECDatabase* lpDatabase, unsigned int ulObjId, unsigned int* lpulSize)
  73. {
  74. ECRESULT er = erSuccess;
  75. DB_RESULT lpDBResult;
  76. DB_ROW lpDBRow = NULL;
  77. unsigned int ulSize = 0;
  78. std::string strQuery;
  79. strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid="+stringify(ulObjId)+" AND ((tag="+stringify(PROP_ID(PR_MESSAGE_SIZE))+" AND type="+stringify(PROP_TYPE(PR_MESSAGE_SIZE))+") OR (tag="+stringify(PROP_ID(PR_ATTACH_SIZE))+" AND type="+stringify(PROP_TYPE(PR_ATTACH_SIZE))+") )" + " LIMIT 1";
  80. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  81. if(er != erSuccess)
  82. return er;
  83. if (lpDatabase->GetNumRows(lpDBResult) != 1)
  84. return KCERR_NOT_FOUND;
  85. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  86. if (lpDBRow == nullptr || lpDBRow[0] == nullptr)
  87. return KCERR_NOT_FOUND;
  88. ulSize = atoi(lpDBRow[0]);
  89. *lpulSize = ulSize;
  90. return erSuccess;
  91. }
  92. ECRESULT CalculateObjectSize(ECDatabase* lpDatabase, unsigned int objid, unsigned int ulObjType, unsigned int* lpulSize)
  93. {
  94. ECRESULT er = erSuccess;
  95. DB_RESULT lpDBResult;
  96. DB_ROW lpDBRow = NULL;
  97. unsigned int ulSize = 0;
  98. std::string strQuery;
  99. object_ptr<ECAttachmentStorage> lpAttachmentStorage;
  100. ECDatabaseAttachment *lpDatabaseStorage = NULL;
  101. *lpulSize = 0;
  102. // strQuery = "SELECT SUM(16 + IF(val_ulong, 4, 0)+IF(val_double||val_longint||val_hi||val_lo, 8, 0)+ LENGTH(IFNULL(val_string, 0))+length(IFNULL(val_binary, 0))) FROM properties WHERE hierarchyid=" + stringify(objid);
  103. // SQLite doesn't support IF-type statements, so we're now using a slightly simpler construct ..
  104. strQuery = "SELECT (SELECT SUM(20 + LENGTH(IFNULL(val_string, 0))+length(IFNULL(val_binary, 0))) FROM properties WHERE hierarchyid=" + stringify(objid) + ") + IFNULL( (SELECT SUM(LENGTH(lob.val_binary)) FROM `lob` JOIN `singleinstances` ON singleinstances.instanceid = lob.instanceid WHERE singleinstances.hierarchyid=" + stringify(objid) + "), 0)";
  105. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  106. if(er != erSuccess)
  107. return er;
  108. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  109. if(lpDBRow == NULL || lpDBRow[0] == NULL)
  110. ulSize = 0;
  111. else
  112. ulSize = atoui(lpDBRow[0])+ 28;// + hierarchy size
  113. er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
  114. if (er != erSuccess)
  115. return er;
  116. // since we already did the length magic in the previous query, we only need the
  117. // extra size for filestorage and S3 storage, i.e. not database storage
  118. lpDatabaseStorage = dynamic_cast<ECDatabaseAttachment *>(lpAttachmentStorage.get());
  119. if (!lpDatabaseStorage) {
  120. size_t ulAttachSize = 0;
  121. er = lpAttachmentStorage->GetSize(objid, PROP_ID(PR_ATTACH_DATA_BIN), &ulAttachSize);
  122. if (er != erSuccess)
  123. return er;
  124. ulSize += ulAttachSize;
  125. }
  126. // Calculate also mv-props
  127. strQuery = "SELECT SUM(20 + LENGTH(IFNULL(val_string, 0))+length(IFNULL(val_binary, 0))) FROM mvproperties WHERE hierarchyid=" + stringify(objid);
  128. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  129. if(er != erSuccess)
  130. return er;
  131. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  132. if(lpDBRow != NULL && lpDBRow[0] != NULL)
  133. ulSize += atoui(lpDBRow[0]); // Add the size
  134. // Get parent sizes
  135. strQuery = "SELECT SUM(IFNULL(p.val_ulong, 0)) FROM hierarchy as h JOIN properties AS p ON hierarchyid=h.id WHERE h.parent=" + stringify(objid)+ " AND ((p.tag="+stringify(PROP_ID(PR_MESSAGE_SIZE))+" AND p.type="+stringify(PROP_TYPE(PR_MESSAGE_SIZE)) + ") || (p.tag="+stringify(PROP_ID(PR_ATTACH_SIZE))+" AND p.type="+stringify(PROP_TYPE(PR_ATTACH_SIZE))+ "))";
  136. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  137. if(er != erSuccess)
  138. return er;
  139. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  140. if(lpDBRow != NULL && lpDBRow[0] != NULL)
  141. ulSize += atoui(lpDBRow[0]); // Add the size
  142. *lpulSize = ulSize;
  143. return erSuccess;
  144. }
  145. ECRESULT UpdateObjectSize(ECDatabase* lpDatabase, unsigned int ulObjId, unsigned int ulObjType, eSizeUpdateAction updateAction, long long llSize)
  146. {
  147. ECRESULT er;
  148. unsigned int ulPropTag = 0;
  149. unsigned int ulAffRows = 0;
  150. std::string strQuery;
  151. std::string strField;
  152. if(ulObjType == MAPI_ATTACH) {
  153. ulPropTag = PR_ATTACH_SIZE;
  154. strField = "val_ulong";
  155. }else if(ulObjType == MAPI_STORE) {
  156. ulPropTag = PR_MESSAGE_SIZE_EXTENDED;
  157. strField = "val_longint";
  158. }else {
  159. ulPropTag = PR_MESSAGE_SIZE;
  160. strField = "val_ulong";
  161. }
  162. if (updateAction == UPDATE_SET) {
  163. strQuery = "REPLACE INTO properties(hierarchyid, tag, type, "+strField+") VALUES(" + stringify(ulObjId) + "," + stringify(PROP_ID(ulPropTag)) + "," + stringify(PROP_TYPE(ulPropTag)) + "," + stringify_int64(llSize) + ")";
  164. er = lpDatabase->DoInsert(strQuery);
  165. if(er != erSuccess)
  166. return er;
  167. if(ulObjType == MAPI_MESSAGE) {
  168. // Update cell cache for new size
  169. sObjectTableKey key;
  170. struct propVal sPropVal;
  171. key.ulObjId = ulObjId;
  172. key.ulOrderId = 0;
  173. sPropVal.ulPropTag = PR_MESSAGE_SIZE;
  174. sPropVal.Value.ul = llSize;
  175. sPropVal.__union = SOAP_UNION_propValData_ul;
  176. er = g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_MESSAGE_SIZE, &sPropVal);
  177. if(er != erSuccess)
  178. return er;
  179. }
  180. } else {
  181. strQuery = "UPDATE properties SET "+strField+"=";
  182. if(updateAction == UPDATE_ADD)
  183. strQuery += strField+"+";
  184. else if(updateAction == UPDATE_SUB)
  185. strQuery += strField+"-";
  186. strQuery += stringify_int64(llSize) +" WHERE tag="+stringify(PROP_ID(ulPropTag))+" AND type="+stringify(PROP_TYPE(ulPropTag)) + " AND hierarchyid="+stringify(ulObjId);
  187. if(updateAction == UPDATE_SUB)
  188. strQuery += " AND "+strField+" >="+stringify_int64(llSize);
  189. er = lpDatabase->DoUpdate(strQuery, &ulAffRows);
  190. if(er != erSuccess)
  191. return er;
  192. if(ulObjType == MAPI_MESSAGE) {
  193. // Update cell cache
  194. sObjectTableKey key;
  195. er = g_lpSessionManager->GetCacheManager()->UpdateCell(ulObjId, PR_MESSAGE_SIZE, (updateAction == UPDATE_ADD ? llSize : -llSize));
  196. if(er != erSuccess)
  197. return er;
  198. }
  199. }
  200. return erSuccess;
  201. }
  202. } /* namespace */