StreamUtil.cpp 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980
  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 <new>
  18. #include <kopano/platform.h>
  19. #include <kopano/memory.hpp>
  20. #include "StreamUtil.h"
  21. #include "StorageUtil.h"
  22. #include "cmdutil.hpp"
  23. #include "ECAttachmentStorage.h"
  24. #include "ECSecurity.h"
  25. #include "ECSessionManager.h"
  26. #include "ECGenProps.h"
  27. #include "ECTPropsPurge.h"
  28. #include "ECICS.h"
  29. #include "ECMemStream.h"
  30. #include <kopano/MAPIErrors.h>
  31. #include <kopano/charset/convert.h>
  32. #include <kopano/charset/utf8string.h>
  33. #include <ECFifoBuffer.h>
  34. #include <ECSerializer.h>
  35. #include <kopano/stringutil.h>
  36. #include <mapitags.h>
  37. #include <kopano/mapiext.h>
  38. #include <mapidefs.h>
  39. #include <edkmdb.h>
  40. #include <set>
  41. #include <map>
  42. #include <string>
  43. /*
  44. streams are as follows:
  45. <version>
  46. <message>
  47. messages are as follows:
  48. <# of props>:32bit
  49. <property>
  50. <# of sub objects>:32bit
  51. <subobject>
  52. properties are as follows:
  53. <prop tag>:32bit
  54. [<length in bytes>:32bit]
  55. <data bytes>
  56. [<guid, kind, [id|bytes+string>]
  57. subobjects are as follows:
  58. <type>:32bit
  59. <id>:32bit
  60. <message>
  61. */
  62. using namespace KCHL;
  63. static inline bool operator<(const GUID &lhs, const GUID &rhs)
  64. {
  65. return memcmp(&lhs, &rhs, sizeof(GUID)) < 0;
  66. }
  67. namespace KC {
  68. class ECStreamSerializer _kc_final : public ECSerializer {
  69. public:
  70. ECStreamSerializer(IStream *lpBuffer);
  71. virtual ECRESULT SetBuffer(void *) _kc_override;
  72. virtual ECRESULT Write(const void *ptr, size_t size, size_t nmemb) _kc_override;
  73. virtual ECRESULT Read(void *ptr, size_t size, size_t nmemb) _kc_override;
  74. virtual ECRESULT Skip(size_t size, size_t nmemb) _kc_override;
  75. virtual ECRESULT Flush(void) _kc_override;
  76. virtual ECRESULT Stat(ULONG *have_read, ULONG *have_written) _kc_override;
  77. private:
  78. IStream *m_lpBuffer;
  79. ULONG m_ulRead = 0, m_ulWritten = 0;
  80. };
  81. const static struct StreamCaps {
  82. bool bSupportUnicode;
  83. } g_StreamCaps[] = {
  84. { false }, // version 0
  85. { true }, // version 1
  86. };
  87. #define FIELD_NR_NAMEID (FIELD_NR_MAX + 1)
  88. #define FIELD_NR_NAMESTR (FIELD_NR_MAX + 2)
  89. #define FIELD_NR_NAMEGUID (FIELD_NR_MAX + 3)
  90. #define STREAM_VERSION 1 // encode strings in UTF-8.
  91. #define STREAM_CAPS_CURRENT (&g_StreamCaps[STREAM_VERSION])
  92. #define CHARSET_WIN1252 "WINDOWS-1252//TRANSLIT"
  93. // External objects
  94. extern ECSessionManager *g_lpSessionManager; // ECServerEntrypoint.cpp
  95. // Helper class for mapping named properties from the stream to local proptags
  96. class NamedPropertyMapper {
  97. public:
  98. NamedPropertyMapper(ECDatabase *lpDatabase);
  99. ECRESULT GetId(const GUID &guid, unsigned int ulNameId, unsigned int *lpId);
  100. ECRESULT GetId(const GUID &guid, const std::string &strNameString, unsigned int *lpId);
  101. private:
  102. typedef std::pair<GUID,unsigned int> nameidkey_t;
  103. typedef std::pair<GUID,std::string> namestringkey_t;
  104. typedef std::map<nameidkey_t,unsigned int> nameidmap_t;
  105. typedef std::map<namestringkey_t,unsigned int> namestringmap_t;
  106. ECDatabase *m_lpDatabase;
  107. nameidmap_t m_mapNameIds;
  108. namestringmap_t m_mapNameStrings;
  109. };
  110. ECStreamSerializer::ECStreamSerializer(IStream *lpBuffer)
  111. {
  112. SetBuffer(lpBuffer);
  113. }
  114. ECRESULT ECStreamSerializer::SetBuffer(void *lpBuffer)
  115. {
  116. m_lpBuffer = static_cast<IStream *>(lpBuffer);
  117. return erSuccess;
  118. }
  119. ECRESULT ECStreamSerializer::Write(const void *ptr, size_t size, size_t nmemb)
  120. {
  121. ECRESULT er = erSuccess;
  122. ULONG cbWritten = 0;
  123. union {
  124. char c[8];
  125. short s;
  126. int i;
  127. long long ll;
  128. } tmp;
  129. if (ptr == NULL)
  130. return KCERR_INVALID_PARAMETER;
  131. switch (size) {
  132. case 1:
  133. er = m_lpBuffer->Write(ptr, nmemb, &cbWritten);
  134. break;
  135. case 2:
  136. for (size_t x = 0; x < nmemb && er == erSuccess; ++x) {
  137. tmp.s = htons(static_cast<const short *>(ptr)[x]);
  138. er = m_lpBuffer->Write(&tmp, size, &cbWritten);
  139. }
  140. break;
  141. case 4:
  142. for (size_t x = 0; x < nmemb && er == erSuccess; ++x) {
  143. tmp.i = htonl(static_cast<const int *>(ptr)[x]);
  144. er = m_lpBuffer->Write(&tmp, size, &cbWritten);
  145. }
  146. break;
  147. case 8:
  148. for (size_t x = 0; x < nmemb && er == erSuccess; ++x) {
  149. tmp.ll = htonll(static_cast<const long long *>(ptr)[x]);
  150. er = m_lpBuffer->Write(&tmp, size, &cbWritten);
  151. }
  152. break;
  153. default:
  154. er = KCERR_INVALID_PARAMETER;
  155. break;
  156. }
  157. m_ulWritten += size * nmemb;
  158. return er;
  159. }
  160. ECRESULT ECStreamSerializer::Read(void *ptr, size_t size, size_t nmemb)
  161. {
  162. ECRESULT er;
  163. ULONG cbRead = 0;
  164. if (ptr == nullptr)
  165. return KCERR_INVALID_PARAMETER;
  166. er = m_lpBuffer->Read(ptr, size * nmemb, &cbRead);
  167. if (er != erSuccess)
  168. return er;
  169. m_ulRead += cbRead;
  170. if (cbRead != size * nmemb)
  171. return KCERR_CALL_FAILED;
  172. switch (size) {
  173. case 1: break;
  174. case 2:
  175. for (size_t x = 0; x < nmemb; ++x)
  176. static_cast<short *>(ptr)[x] = ntohs(static_cast<short *>(ptr)[x]);
  177. break;
  178. case 4:
  179. for (size_t x = 0; x < nmemb; ++x)
  180. static_cast<int *>(ptr)[x] = ntohl(static_cast<int *>(ptr)[x]);
  181. break;
  182. case 8:
  183. for (size_t x = 0; x < nmemb; ++x)
  184. static_cast<long long *>(ptr)[x] = ntohll(static_cast<long long *>(ptr)[x]);
  185. break;
  186. default:
  187. er = KCERR_INVALID_PARAMETER;
  188. break;
  189. }
  190. return er;
  191. }
  192. ECRESULT ECStreamSerializer::Skip(size_t size, size_t nmemb)
  193. {
  194. ECRESULT er = erSuccess;
  195. char buffer[4096];
  196. ULONG read = 0;
  197. size_t total = 0;
  198. while (total < nmemb * size) {
  199. er = m_lpBuffer->Read(buffer, std::min(sizeof(buffer), (size * nmemb) - total), &read);
  200. if (er != erSuccess)
  201. return er;
  202. total += read;
  203. }
  204. return er;
  205. }
  206. ECRESULT ECStreamSerializer::Flush()
  207. {
  208. ECRESULT er;
  209. ULONG cbRead = 0;
  210. char buf[16384];
  211. while (true) {
  212. er = m_lpBuffer->Read(buf, sizeof(buf), &cbRead);
  213. if (er != erSuccess)
  214. return er;
  215. m_ulRead += cbRead;
  216. if (cbRead < sizeof(buf))
  217. break;
  218. }
  219. return er;
  220. }
  221. ECRESULT ECStreamSerializer::Stat(ULONG *lpcbRead, ULONG *lpcbWrite)
  222. {
  223. if (lpcbRead != nullptr)
  224. *lpcbRead = m_ulRead;
  225. if (lpcbWrite != nullptr)
  226. *lpcbWrite = m_ulWritten;
  227. return erSuccess;
  228. }
  229. NamedPropertyMapper::NamedPropertyMapper(ECDatabase *lpDatabase)
  230. : m_lpDatabase(lpDatabase)
  231. {
  232. assert(lpDatabase != NULL);
  233. }
  234. ECRESULT NamedPropertyMapper::GetId(const GUID &guid, unsigned int ulNameId, unsigned int *lpulId)
  235. {
  236. ECRESULT er = erSuccess;
  237. std::string strQuery;
  238. DB_RESULT lpResult;
  239. DB_ROW lpRow = NULL;
  240. nameidkey_t key(guid, ulNameId);
  241. nameidmap_t::const_iterator i = m_mapNameIds.find(key);
  242. if (i != m_mapNameIds.cend()) {
  243. *lpulId = i->second;
  244. return erSuccess;
  245. }
  246. // Check the database
  247. strQuery =
  248. "SELECT id FROM names "
  249. "WHERE nameid=" + stringify(ulNameId) +
  250. " AND guid=" + m_lpDatabase->EscapeBinary((unsigned char*)&guid, sizeof(guid));
  251. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  252. if (er != erSuccess)
  253. return er;
  254. if ((lpRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
  255. if (lpRow[0] == NULL) {
  256. ec_log_err("NamedPropertyMapper::GetId(): column null");
  257. return KCERR_DATABASE_ERROR;
  258. }
  259. *lpulId = atoui((char*)lpRow[0]) + 0x8501;
  260. } else {
  261. // Create the named property
  262. strQuery =
  263. "INSERT INTO names (nameid, guid) "
  264. "VALUES(" + stringify(ulNameId) + "," + m_lpDatabase->EscapeBinary((unsigned char*)&guid, sizeof(guid)) + ")";
  265. er = m_lpDatabase->DoInsert(strQuery, lpulId);
  266. if (er != erSuccess)
  267. return er;
  268. *lpulId += 0x8501;
  269. }
  270. // *lpulId now contains the local propid, update the cache
  271. m_mapNameIds.insert(nameidmap_t::value_type(key, *lpulId));
  272. return erSuccess;
  273. }
  274. ECRESULT NamedPropertyMapper::GetId(const GUID &guid, const std::string &strNameString, unsigned int *lpulId)
  275. {
  276. ECRESULT er = erSuccess;
  277. std::string strQuery;
  278. DB_RESULT lpResult;
  279. DB_ROW lpRow = NULL;
  280. namestringkey_t key(guid, strNameString);
  281. namestringmap_t::const_iterator i = m_mapNameStrings.find(key);
  282. if (i != m_mapNameStrings.cend()) {
  283. *lpulId = i->second;
  284. return erSuccess;
  285. }
  286. // Check the database
  287. strQuery =
  288. "SELECT id FROM names "
  289. "WHERE namestring='" + m_lpDatabase->Escape(strNameString) + "'"
  290. " AND guid=" + m_lpDatabase->EscapeBinary((unsigned char*)&guid, sizeof(guid));
  291. er = m_lpDatabase->DoSelect(strQuery, &lpResult);
  292. if (er != erSuccess)
  293. return er;
  294. if ((lpRow = m_lpDatabase->FetchRow(lpResult)) != NULL) {
  295. if (lpRow[0] == NULL) {
  296. ec_log_err("NamedPropertyMapper::GetId(): column null");
  297. return KCERR_DATABASE_ERROR;
  298. }
  299. *lpulId = atoui((char*)lpRow[0]) + 0x8501;
  300. } else {
  301. // Create the named property
  302. strQuery =
  303. "INSERT INTO names (namestring, guid) "
  304. "VALUES('" + m_lpDatabase->Escape(strNameString) + "'," + m_lpDatabase->EscapeBinary((unsigned char*)&guid, sizeof(guid)) + ")";
  305. er = m_lpDatabase->DoInsert(strQuery, lpulId);
  306. if (er != erSuccess)
  307. return er;
  308. *lpulId += 0x8501;
  309. }
  310. // *lpulId now contains the local propid, update the cache
  311. m_mapNameStrings.insert(namestringmap_t::value_type(key, *lpulId));
  312. return erSuccess;
  313. }
  314. // Utility Functions
  315. static ECRESULT GetValidatedPropType(DB_ROW, unsigned int *type);
  316. static ECRESULT SerializeDatabasePropVal(const StreamCaps *lpStreamCaps,
  317. DB_ROW lpRow, DB_LENGTHS lpLen, ECSerializer *lpSink)
  318. {
  319. ECRESULT er = erSuccess;
  320. unsigned int type = 0;
  321. unsigned int ulPropTag = 0;
  322. unsigned int ulCount;
  323. unsigned int ulLen;
  324. unsigned int ulLastPos;
  325. unsigned int ulLastPos2;
  326. std::string strData;
  327. unsigned int ulKind;
  328. unsigned int ulNameId;
  329. std::string strNameString;
  330. locale_t loc = createlocale(LC_NUMERIC, "C");
  331. convert_context converter;
  332. short i;
  333. unsigned int ul;
  334. float flt;
  335. unsigned char b;
  336. double dbl;
  337. hiloLong hilo;
  338. long long li;
  339. er = GetValidatedPropType(lpRow, &type);
  340. if (er == KCERR_DATABASE_ERROR) {
  341. er = erSuccess;
  342. goto exit;
  343. }
  344. else if (er != erSuccess) {
  345. goto exit;
  346. }
  347. ulPropTag = PROP_TAG(type, atoi(lpRow[FIELD_NR_TAG]));
  348. er = lpSink->Write(&ulPropTag, sizeof(ulPropTag), 1);
  349. if (er != erSuccess)
  350. goto exit;
  351. switch (type) {
  352. case PT_I2:
  353. if (lpRow[FIELD_NR_ULONG] == NULL) {
  354. er = KCERR_NOT_FOUND;
  355. goto exit;
  356. }
  357. i = (short)atoi(lpRow[FIELD_NR_ULONG]);
  358. er = lpSink->Write(&i, sizeof(i), 1);
  359. break;
  360. case PT_LONG:
  361. if (lpRow[FIELD_NR_ULONG] == NULL) {
  362. er = KCERR_NOT_FOUND;
  363. goto exit;
  364. }
  365. ul = atoui(lpRow[FIELD_NR_ULONG]);
  366. er = lpSink->Write(&ul, sizeof(ul), 1);
  367. break;
  368. case PT_R4:
  369. if (lpRow[FIELD_NR_DOUBLE] == NULL) {
  370. er = KCERR_NOT_FOUND;
  371. goto exit;
  372. }
  373. flt = (float)strtod_l(lpRow[FIELD_NR_DOUBLE], NULL, loc);
  374. er = lpSink->Write(&flt, sizeof(flt), 1);
  375. break;
  376. case PT_BOOLEAN:
  377. if (lpRow[FIELD_NR_ULONG] == NULL) {
  378. er = KCERR_NOT_FOUND;
  379. goto exit;
  380. }
  381. b = (atoi(lpRow[FIELD_NR_ULONG]) ? 1 : 0);
  382. er = lpSink->Write(&b, sizeof(b), 1);
  383. break;
  384. case PT_DOUBLE:
  385. case PT_APPTIME:
  386. if (lpRow[FIELD_NR_DOUBLE] == NULL) {
  387. er = KCERR_NOT_FOUND;
  388. goto exit;
  389. }
  390. dbl = strtod_l(lpRow[FIELD_NR_DOUBLE], NULL, loc);
  391. er = lpSink->Write(&dbl, sizeof(dbl), 1);
  392. break;
  393. case PT_CURRENCY:
  394. case PT_SYSTIME:
  395. if (lpRow[FIELD_NR_HI] == NULL || lpRow[FIELD_NR_LO] == NULL) {
  396. er = KCERR_NOT_FOUND;
  397. goto exit;
  398. }
  399. hilo.hi = atoi(lpRow[FIELD_NR_HI]);
  400. hilo.lo = atoui(lpRow[FIELD_NR_LO]);
  401. er = lpSink->Write(&hilo.hi, sizeof(hilo.hi), 1);
  402. if (er == erSuccess)
  403. er = lpSink->Write(&hilo.lo, sizeof(hilo.lo), 1);
  404. break;
  405. case PT_I8:
  406. if (lpRow[FIELD_NR_LONGINT] == NULL) {
  407. er = KCERR_NOT_FOUND;
  408. goto exit;
  409. }
  410. li = atoll(lpRow[FIELD_NR_LONGINT]);
  411. er = lpSink->Write(&li, sizeof(li), 1);
  412. break;
  413. case PT_STRING8:
  414. case PT_UNICODE:
  415. if (lpRow[FIELD_NR_STRING] == NULL) {
  416. er = KCERR_NOT_FOUND;
  417. goto exit;
  418. }
  419. if (lpStreamCaps->bSupportUnicode) {
  420. ulLen = lpLen[FIELD_NR_STRING];
  421. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  422. if (er == erSuccess)
  423. er = lpSink->Write(lpRow[FIELD_NR_STRING], 1, lpLen[FIELD_NR_STRING]);
  424. } else {
  425. const std::string strEncoded = converter.convert_to<std::string>(CHARSET_WIN1252, lpRow[FIELD_NR_STRING], lpLen[FIELD_NR_STRING], "UTF-8");
  426. ulLen = strEncoded.length();
  427. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  428. if (er == erSuccess)
  429. er = lpSink->Write(strEncoded.data(), 1, ulLen);
  430. }
  431. break;
  432. case PT_CLSID:
  433. case PT_BINARY:
  434. if (lpRow[FIELD_NR_BINARY] == NULL) {
  435. er = KCERR_NOT_FOUND;
  436. goto exit;
  437. }
  438. ulLen = lpLen[FIELD_NR_BINARY];
  439. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  440. if (er == erSuccess)
  441. er = lpSink->Write(lpRow[FIELD_NR_BINARY], 1, lpLen[FIELD_NR_BINARY]);
  442. break;
  443. case PT_MV_I2:
  444. if (lpRow[FIELD_NR_ULONG] == NULL) {
  445. er = KCERR_NOT_FOUND;
  446. goto exit;
  447. }
  448. ulCount = atoi(lpRow[FIELD_NR_ID]);
  449. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  450. ulLastPos = 0;
  451. for (unsigned int x = 0; er == erSuccess && x < ulCount; ++x) {
  452. ParseMVProp(lpRow[FIELD_NR_ULONG], lpLen[FIELD_NR_ULONG], &ulLastPos, &strData);
  453. i = (short)atoi(strData.c_str());
  454. er = lpSink->Write(&i, sizeof(i), 1);
  455. }
  456. break;
  457. case PT_MV_LONG:
  458. if (lpRow[FIELD_NR_ULONG] == NULL) {
  459. er = KCERR_NOT_FOUND;
  460. goto exit;
  461. }
  462. ulCount = atoi(lpRow[FIELD_NR_ID]);
  463. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  464. ulLastPos = 0;
  465. for (unsigned int x = 0; er == erSuccess && x < ulCount; ++x) {
  466. ParseMVProp(lpRow[FIELD_NR_ULONG], lpLen[FIELD_NR_ULONG], &ulLastPos, &strData);
  467. ul = atoui((char*)strData.c_str());
  468. er = lpSink->Write(&ul, sizeof(ul), 1);
  469. }
  470. break;
  471. case PT_MV_R4:
  472. if (lpRow[FIELD_NR_DOUBLE] == NULL) {
  473. er = KCERR_NOT_FOUND;
  474. goto exit;
  475. }
  476. ulCount = atoi(lpRow[FIELD_NR_ID]);
  477. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  478. ulLastPos = 0;
  479. for (unsigned int x = 0; er == erSuccess && x < ulCount; ++x) {
  480. ParseMVProp(lpRow[FIELD_NR_DOUBLE], lpLen[FIELD_NR_DOUBLE], &ulLastPos, &strData);
  481. flt = (float)strtod_l(strData.c_str(), NULL, loc);
  482. er = lpSink->Write(&flt, sizeof(flt), 1);
  483. }
  484. break;
  485. case PT_MV_DOUBLE:
  486. case PT_MV_APPTIME:
  487. if (lpRow[FIELD_NR_DOUBLE] == NULL) {
  488. er = KCERR_NOT_FOUND;
  489. goto exit;
  490. }
  491. ulCount = atoi(lpRow[FIELD_NR_ID]);
  492. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  493. ulLastPos = 0;
  494. for (unsigned int x = 0; er == erSuccess && x < ulCount; ++x) {
  495. ParseMVProp(lpRow[FIELD_NR_DOUBLE], lpLen[FIELD_NR_DOUBLE], &ulLastPos, &strData);
  496. dbl = strtod_l(strData.c_str(), NULL, loc);
  497. er = lpSink->Write(&dbl, sizeof(dbl), 1);
  498. }
  499. break;
  500. case PT_MV_CURRENCY:
  501. case PT_MV_SYSTIME:
  502. if (lpRow[FIELD_NR_HI] == NULL || lpRow[FIELD_NR_LO] == NULL) {
  503. er = KCERR_NOT_FOUND;
  504. goto exit;
  505. }
  506. ulCount = atoi(lpRow[FIELD_NR_ID]);
  507. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  508. ulLastPos = 0;
  509. ulLastPos2 = 0;
  510. for (unsigned int x = 0; er == erSuccess && x < ulCount; ++x) {
  511. ParseMVProp(lpRow[FIELD_NR_LO], lpLen[FIELD_NR_LO], &ulLastPos, &strData);
  512. hilo.lo = atoui((char*)strData.c_str());
  513. ParseMVProp(lpRow[FIELD_NR_HI], lpLen[FIELD_NR_HI], &ulLastPos2, &strData);
  514. hilo.hi = atoi((char*)strData.c_str());
  515. er = lpSink->Write(&hilo.hi, sizeof(hilo.hi), 1);
  516. if (er == erSuccess)
  517. er = lpSink->Write(&hilo.lo, sizeof(hilo.lo), 1);
  518. }
  519. break;
  520. case PT_MV_BINARY:
  521. case PT_MV_CLSID:
  522. if (lpRow[FIELD_NR_BINARY] == NULL) {
  523. er = KCERR_NOT_FOUND;
  524. goto exit;
  525. }
  526. ulCount = atoi(lpRow[FIELD_NR_ID]);
  527. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  528. ulLastPos = 0;
  529. for (unsigned int x = 0; er == erSuccess && x < ulCount; ++x) {
  530. ParseMVProp(lpRow[FIELD_NR_BINARY], lpLen[FIELD_NR_BINARY], &ulLastPos, &strData);
  531. ulLen = (unsigned int)strData.size();
  532. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  533. if (er == erSuccess)
  534. er = lpSink->Write(strData.c_str(), 1, ulLen);
  535. }
  536. break;
  537. case PT_MV_STRING8:
  538. case PT_MV_UNICODE:
  539. if (lpRow[FIELD_NR_STRING] == NULL) {
  540. er = KCERR_NOT_FOUND;
  541. goto exit;
  542. }
  543. ulCount = atoi(lpRow[FIELD_NR_ID]);
  544. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  545. ulLastPos = 0;
  546. for (unsigned int x = 0; er == erSuccess && x < ulCount; ++x) {
  547. ParseMVProp(lpRow[FIELD_NR_STRING], lpLen[FIELD_NR_STRING], &ulLastPos, &strData);
  548. if (lpStreamCaps->bSupportUnicode) {
  549. ulLen = (unsigned int)strData.size();
  550. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  551. if (er == erSuccess)
  552. er = lpSink->Write(strData.c_str(), 1, ulLen);
  553. } else {
  554. const std::string strEncoded = converter.convert_to<std::string>(CHARSET_WIN1252, strData, rawsize(strData), "UTF-8");
  555. ulLen = (unsigned int)strEncoded.size();
  556. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  557. if (er == erSuccess)
  558. er = lpSink->Write(strEncoded.c_str(), 1, ulLen);
  559. }
  560. }
  561. break;
  562. case PT_MV_I8:
  563. if (lpRow[FIELD_NR_LONGINT] == NULL) {
  564. er = KCERR_NOT_FOUND;
  565. goto exit;
  566. }
  567. ulCount = atoi(lpRow[FIELD_NR_ID]);
  568. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  569. ulLastPos = 0;
  570. for (unsigned int x = 0; er == erSuccess && x < ulCount; ++x) {
  571. ParseMVProp(lpRow[FIELD_NR_LONGINT], lpLen[FIELD_NR_LONGINT], &ulLastPos, &strData);
  572. li = atoll(strData.c_str());
  573. er = lpSink->Write(&li, sizeof(li), 1);
  574. }
  575. break;
  576. default:
  577. er = KCERR_INVALID_TYPE;
  578. goto exit;
  579. }
  580. // If property is named property in the dynamic range we need to add some extra info
  581. if (PROP_ID(ulPropTag) > 0x8500) {
  582. // Send out the GUID.
  583. er = lpSink->Write(lpRow[FIELD_NR_NAMEGUID], 1, lpLen[FIELD_NR_NAMEGUID]);
  584. if (er == erSuccess && lpRow[FIELD_NR_NAMEID] != NULL) {
  585. ulKind = MNID_ID;
  586. ulNameId = atoui((char*)lpRow[FIELD_NR_NAMEID]);
  587. er = lpSink->Write(&ulKind, sizeof(ulKind), 1);
  588. if (er == erSuccess)
  589. er = lpSink->Write(&ulNameId, sizeof(ulNameId), 1);
  590. } else if (er == erSuccess && lpRow[FIELD_NR_NAMESTR] != NULL) {
  591. ulKind = MNID_STRING;
  592. ulLen = lpLen[FIELD_NR_NAMESTR];
  593. er = lpSink->Write(&ulKind, sizeof(ulKind), 1);
  594. if (er == erSuccess)
  595. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  596. if (er == erSuccess)
  597. er = lpSink->Write(lpRow[FIELD_NR_NAMESTR], 1, ulLen);
  598. } else if (er == erSuccess)
  599. er = KCERR_INVALID_TYPE;
  600. }
  601. exit:
  602. freelocale(loc);
  603. return er;
  604. }
  605. static ECRESULT SerializePropVal(const StreamCaps *lpStreamCaps,
  606. const struct propVal &sPropVal, ECSerializer *lpSink,
  607. const NamedPropDefMap *lpNamedPropDefs)
  608. {
  609. ECRESULT er;
  610. unsigned int type = PROP_TYPE(sPropVal.ulPropTag);
  611. unsigned int ulLen;
  612. unsigned char b;
  613. unsigned int ulPropTag = sPropVal.ulPropTag;
  614. convert_context converter;
  615. NamedPropDefMap::const_iterator iNamedPropDef;
  616. // We always stream PT_STRING8
  617. if(PROP_TYPE(ulPropTag) == PT_UNICODE)
  618. ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_STRING8);
  619. else if(PROP_TYPE(ulPropTag) == PT_MV_UNICODE)
  620. ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_MV_STRING8);
  621. if (PROP_ID(ulPropTag) > 0x8500) {
  622. assert(lpNamedPropDefs != NULL);
  623. if (!lpNamedPropDefs)
  624. return KCERR_INVALID_TYPE;
  625. iNamedPropDef = lpNamedPropDefs->find(ulPropTag);
  626. assert(iNamedPropDef != lpNamedPropDefs->cend());
  627. if (iNamedPropDef == lpNamedPropDefs->cend())
  628. return KCERR_NOT_FOUND;
  629. }
  630. er = lpSink->Write((unsigned char *)&ulPropTag, sizeof(ulPropTag), 1);
  631. if (er != erSuccess)
  632. return er;
  633. switch (type) {
  634. case PT_I2:
  635. er = lpSink->Write(&sPropVal.Value.i, sizeof(sPropVal.Value.i), 1);
  636. break;
  637. case PT_LONG:
  638. er = lpSink->Write(&sPropVal.Value.ul, sizeof(sPropVal.Value.ul), 1);
  639. break;
  640. case PT_R4:
  641. er = lpSink->Write(&sPropVal.Value.flt, sizeof(sPropVal.Value.flt), 1);
  642. break;
  643. case PT_BOOLEAN:
  644. b = (sPropVal.Value.b ? 1 : 0);
  645. er = lpSink->Write(&b, sizeof(b), 1);
  646. break;
  647. case PT_DOUBLE:
  648. case PT_APPTIME:
  649. er = lpSink->Write(&sPropVal.Value.dbl, sizeof(sPropVal.Value.dbl), 1);
  650. break;
  651. case PT_CURRENCY:
  652. case PT_SYSTIME:
  653. er = lpSink->Write(&sPropVal.Value.hilo->hi, sizeof(sPropVal.Value.hilo->hi), 1);
  654. if (er == erSuccess)
  655. er = lpSink->Write(&sPropVal.Value.hilo->lo, sizeof(sPropVal.Value.hilo->lo), 1);
  656. break;
  657. case PT_I8:
  658. er = lpSink->Write(&sPropVal.Value.li, sizeof(sPropVal.Value.li), 1);
  659. break;
  660. case PT_STRING8:
  661. case PT_UNICODE:
  662. if (lpStreamCaps->bSupportUnicode) {
  663. ulLen = (unsigned)strlen(sPropVal.Value.lpszA);
  664. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  665. if (er == erSuccess)
  666. er = lpSink->Write(sPropVal.Value.lpszA, 1, ulLen);
  667. } else {
  668. const std::string strEncoded = converter.convert_to<std::string>(CHARSET_WIN1252, sPropVal.Value.lpszA, rawsize(sPropVal.Value.lpszA), "UTF-8");
  669. ulLen = strEncoded.length();
  670. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  671. if (er == erSuccess)
  672. er = lpSink->Write(strEncoded.data(), 1, ulLen);
  673. }
  674. break;
  675. case PT_CLSID:
  676. case PT_BINARY:
  677. er = lpSink->Write(&sPropVal.Value.bin->__size, sizeof(sPropVal.Value.bin->__size), 1);
  678. if (er == erSuccess)
  679. er = lpSink->Write(sPropVal.Value.bin->__ptr, 1, sPropVal.Value.bin->__size);
  680. break;
  681. case PT_MV_I2:
  682. er = lpSink->Write(&sPropVal.Value.mvi.__size, sizeof(sPropVal.Value.mvi.__size), 1);
  683. for (gsoap_size_t x = 0; er == erSuccess && x < sPropVal.Value.mvi.__size; ++x)
  684. er = lpSink->Write(&sPropVal.Value.mvi.__ptr[x], sizeof(sPropVal.Value.mvi.__ptr[x]), 1);
  685. break;
  686. case PT_MV_LONG:
  687. er = lpSink->Write(&sPropVal.Value.mvl.__size, sizeof(sPropVal.Value.mvl.__size), 1);
  688. for (gsoap_size_t x = 0; er == erSuccess && x < sPropVal.Value.mvl.__size; ++x)
  689. er = lpSink->Write(&sPropVal.Value.mvl.__ptr[x], sizeof(sPropVal.Value.mvl.__ptr[x]), 1);
  690. break;
  691. case PT_MV_R4:
  692. er = lpSink->Write(&sPropVal.Value.mvflt.__size, sizeof(sPropVal.Value.mvflt.__size), 1);
  693. for (gsoap_size_t x = 0; er == erSuccess && x < sPropVal.Value.mvflt.__size; ++x)
  694. er = lpSink->Write(&sPropVal.Value.mvflt.__ptr[x], sizeof(sPropVal.Value.mvflt.__ptr[x]), 1);
  695. break;
  696. case PT_MV_DOUBLE:
  697. case PT_MV_APPTIME:
  698. er = lpSink->Write(&sPropVal.Value.mvdbl.__size, sizeof(sPropVal.Value.mvdbl.__size), 1);
  699. for (gsoap_size_t x = 0; er == erSuccess && x < sPropVal.Value.mvdbl.__size; ++x)
  700. er = lpSink->Write(&sPropVal.Value.mvdbl.__ptr[x], sizeof(sPropVal.Value.mvdbl.__ptr[x]), 1);
  701. break;
  702. case PT_MV_CURRENCY:
  703. case PT_MV_SYSTIME:
  704. er = lpSink->Write(&sPropVal.Value.mvhilo.__size, sizeof(sPropVal.Value.mvhilo.__size), 1);
  705. for (gsoap_size_t x = 0; er == erSuccess && x < sPropVal.Value.mvhilo.__size; ++x) {
  706. er = lpSink->Write(&sPropVal.Value.mvhilo.__ptr[x].hi, sizeof(sPropVal.Value.mvhilo.__ptr[x].hi), 1);
  707. if (er == erSuccess)
  708. er = lpSink->Write(&sPropVal.Value.mvhilo.__ptr[x].lo, sizeof(sPropVal.Value.mvhilo.__ptr[x].lo), 1);
  709. }
  710. break;
  711. case PT_MV_BINARY:
  712. case PT_MV_CLSID:
  713. er = lpSink->Write(&sPropVal.Value.mvbin.__size, sizeof(sPropVal.Value.mvbin.__size), 1);
  714. for (gsoap_size_t x = 0; er == erSuccess && x < sPropVal.Value.mvbin.__size; ++x) {
  715. er = lpSink->Write(&sPropVal.Value.mvbin.__ptr[x].__size, sizeof(sPropVal.Value.mvbin.__ptr[x].__size), 1);
  716. if (er == erSuccess)
  717. er = lpSink->Write(sPropVal.Value.mvbin.__ptr[x].__ptr, 1, sPropVal.Value.mvbin.__ptr[x].__size);
  718. }
  719. break;
  720. case PT_MV_STRING8:
  721. case PT_MV_UNICODE:
  722. er = lpSink->Write(&sPropVal.Value.mvszA.__size, sizeof(sPropVal.Value.mvszA.__size), 1);
  723. for (gsoap_size_t x = 0; er == erSuccess && x < sPropVal.Value.mvszA.__size; ++x) {
  724. if (lpStreamCaps->bSupportUnicode) {
  725. ulLen = (unsigned)strlen(sPropVal.Value.mvszA.__ptr[x]);
  726. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  727. if (er == erSuccess)
  728. er = lpSink->Write(sPropVal.Value.mvszA.__ptr[x], 1, ulLen);
  729. } else {
  730. const std::string strEncoded = converter.convert_to<std::string>(CHARSET_WIN1252, sPropVal.Value.mvszA.__ptr[x], rawsize(sPropVal.Value.mvszA.__ptr[x]), "UTF-8");
  731. ulLen = strEncoded.length();
  732. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  733. if (er == erSuccess)
  734. er = lpSink->Write(strEncoded.data(), 1, ulLen);
  735. }
  736. }
  737. break;
  738. case PT_MV_I8:
  739. er = lpSink->Write(&sPropVal.Value.mvli.__size, sizeof(sPropVal.Value.mvli.__size), 1);
  740. for (gsoap_size_t x = 0; er == erSuccess && x < sPropVal.Value.mvli.__size; ++x)
  741. er = lpSink->Write(&sPropVal.Value.mvli.__ptr[x], sizeof(sPropVal.Value.mvli.__ptr[x]), 1);
  742. break;
  743. default:
  744. er = KCERR_INVALID_TYPE;
  745. }
  746. // If property is named property in the dynamic range we need to add some extra info
  747. if (PROP_ID(sPropVal.ulPropTag) > 0x8500) {
  748. assert(lpNamedPropDefs != NULL && iNamedPropDef != lpNamedPropDefs->cend());
  749. // Send out the GUID.
  750. er = lpSink->Write(&iNamedPropDef->second.guid, 1, sizeof(iNamedPropDef->second.guid));
  751. if (er == erSuccess)
  752. er = lpSink->Write(&iNamedPropDef->second.ulKind, sizeof(iNamedPropDef->second.ulKind), 1);
  753. if (er == erSuccess) {
  754. if (iNamedPropDef->second.ulKind == MNID_ID)
  755. er = lpSink->Write(&iNamedPropDef->second.ulId, sizeof(iNamedPropDef->second.ulId), 1);
  756. else if ( iNamedPropDef->second.ulKind == MNID_STRING) {
  757. unsigned int ulLen = iNamedPropDef->second.strName.size();
  758. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  759. if (er == erSuccess)
  760. er = lpSink->Write(iNamedPropDef->second.strName.data(), 1, ulLen);
  761. }
  762. else
  763. er = KCERR_INVALID_TYPE;
  764. }
  765. }
  766. return er;
  767. }
  768. static ECRESULT SerializeProps(struct propValArray *lpPropVals,
  769. LPCSTREAMCAPS lpStreamCaps, ECSerializer *lpSink,
  770. const NamedPropDefMap *lpNamedPropDefs)
  771. {
  772. ECRESULT er;
  773. unsigned int ulCount = 0;
  774. ulCount = lpPropVals->__size;
  775. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  776. if (er != erSuccess)
  777. return er;
  778. for (unsigned int i = 0; i < ulCount; ++i) {
  779. er = SerializePropVal(lpStreamCaps, lpPropVals->__ptr[i], lpSink, lpNamedPropDefs);
  780. if (er != erSuccess)
  781. return er;
  782. }
  783. return erSuccess;
  784. }
  785. static ECRESULT GetBestBody(ECDatabase *lpDatabase, unsigned int ulObjId,
  786. string *lpstrBestBody)
  787. {
  788. ECRESULT er = erSuccess;
  789. DB_ROW lpDBRow = NULL;
  790. DB_RESULT lpDBResult;
  791. string strQuery;
  792. strQuery = "SELECT tag FROM properties WHERE hierarchyid=" + stringify(ulObjId) + " AND tag IN (0x1009, 0x1013) ORDER BY tag LIMIT 1";
  793. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  794. if (er != erSuccess)
  795. return er;
  796. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  797. if (lpDBRow && lpDBRow[0])
  798. *lpstrBestBody = lpDBRow[0];
  799. else
  800. *lpstrBestBody = "0";
  801. return erSuccess;
  802. }
  803. static ECRESULT SerializeProps(ECSession *lpecSession, ECDatabase *lpDatabase,
  804. ECAttachmentStorage *lpAttachmentStorage, LPCSTREAMCAPS lpStreamCaps,
  805. unsigned int ulObjId, unsigned int ulObjType, unsigned int ulStoreId,
  806. GUID *lpsGuid, ULONG ulFlags, ECSerializer *lpSink, bool bTop)
  807. {
  808. ECRESULT er = erSuccess;
  809. unsigned int ulCount = 0;
  810. struct soap *soap = NULL;
  811. struct propVal sPropVal{__gszeroinit};
  812. DB_ROW lpDBRow = NULL;
  813. DB_LENGTHS lpDBLen = NULL;
  814. DB_RESULT lpDBResult;
  815. std::string strQuery;
  816. object_ptr<ECMemStream> lpStream;
  817. object_ptr<IStream> lpIStream;
  818. ECStreamSerializer * lpTempSink = NULL;
  819. bool bUseSQLMulti = parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_sql_procedures"));
  820. std::list<struct propVal> sPropValList;
  821. assert(lpStreamCaps != NULL);
  822. er = ECMemStream::Create(nullptr, 0, STGM_SHARE_EXCLUSIVE | STGM_WRITE, nullptr, nullptr, nullptr, &~lpStream);
  823. if (er != erSuccess)
  824. goto exit;
  825. er = lpStream->QueryInterface(IID_IStream, &~lpIStream);
  826. if (er != erSuccess)
  827. goto exit;
  828. if (!lpAttachmentStorage) {
  829. er = KCERR_INVALID_PARAMETER;
  830. goto exit;
  831. }
  832. lpTempSink = new(std::nothrow) ECStreamSerializer(lpIStream);
  833. if (lpTempSink == nullptr)
  834. return KCERR_NOT_ENOUGH_MEMORY;
  835. // We'll (ab)use a soap structure as a memory pool.
  836. soap = soap_new();
  837. // PR_SOURCE_KEY
  838. if (bTop && ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_SOURCE_KEY, ulObjId, 0, ulStoreId, 0, ulObjType, &sPropVal) == erSuccess)
  839. sPropValList.push_back(sPropVal);
  840. if (bUseSQLMulti) {
  841. er = lpDatabase->GetNextResult(&lpDBResult);
  842. } else {
  843. // szGetProps
  844. string strMode = "0";
  845. string strBestBody = "0";
  846. if(ulFlags & SYNC_BEST_BODY) {
  847. strMode = "1";
  848. er = GetBestBody(lpDatabase, ulObjId, &strBestBody);
  849. if (er != erSuccess)
  850. goto exit;
  851. } else if(ulFlags & SYNC_LIMITED_IMESSAGE)
  852. strMode = "2";
  853. strQuery = "SELECT " PROPCOLORDER ", 0, names.nameid, names.namestring, names.guid FROM properties "
  854. "LEFT JOIN names ON (properties.tag-0x8501)=names.id WHERE hierarchyid=" + stringify(ulObjId) + " AND (tag <= 0x8500 OR names.id IS NOT NULL) "
  855. "AND (tag NOT IN (0x1009, 0x1013) OR " + strMode + " = 0 OR (" + strMode + " = 1 AND tag = " + strBestBody + ") ) "
  856. "UNION "
  857. "SELECT " MVPROPCOLORDER ", 0, names.nameid, names.namestring, names.guid FROM mvproperties "
  858. "LEFT JOIN names ON (mvproperties.tag-0x8501)=names.id WHERE hierarchyid=" + stringify(ulObjId) + " AND (tag <= 0x8500 OR names.id IS NOT NULL) "
  859. "GROUP BY tag, mvproperties.type"
  860. ;
  861. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  862. }
  863. if (er != erSuccess)
  864. goto exit;
  865. // Properties
  866. while ((lpDBRow = lpDatabase->FetchRow(lpDBResult)) != NULL) {
  867. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  868. if (lpDBRow == NULL || lpDBLen == NULL) {
  869. er = KCERR_DATABASE_ERROR;
  870. ec_log_err("SerializeProps(): fetchrow/fetchrowlengths failed");
  871. goto exit;
  872. }
  873. er = SerializeDatabasePropVal(lpStreamCaps, lpDBRow, lpDBLen, lpTempSink);
  874. if (er != erSuccess)
  875. goto exit;
  876. ++ulCount;
  877. }
  878. for (const auto &pv : sPropValList) {
  879. /* No NamedPropDefMap needed for computed properties */
  880. er = SerializePropVal(lpStreamCaps, pv, lpTempSink, NULL);
  881. if (er != erSuccess)
  882. goto exit;
  883. ++ulCount;
  884. }
  885. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  886. if (er != erSuccess)
  887. goto exit;
  888. er = lpSink->Write(lpStream->GetBuffer(), 1, lpStream->GetSize());
  889. if (er != erSuccess)
  890. goto exit;
  891. exit:
  892. delete lpTempSink;
  893. if (soap) {
  894. soap_destroy(soap);
  895. soap_end(soap);
  896. soap_free(soap);
  897. }
  898. return er;
  899. }
  900. /**
  901. * Serialize a Message directly from the database.
  902. * This method handles direct subobjects and recurses whenever an embedded
  903. * message is encountered.
  904. *
  905. * @param[in] lpecSession Pointer to the current session.
  906. * @param[in] lpStreamDatabase Pointer to the database.
  907. * @param[in] lpAttachmentStorage Pointer to the attachmentstore.
  908. * @param[in] lpStreamCaps Pointer to a stream capability structore. Must be NULL except
  909. * when called by SerializeMessage itself.
  910. * @param[in] ulObjId The hierarchyid of the object to serialize.
  911. * @param[in] ulObjType The type of the object to serialize. Must be MAPI_MESSAGE.
  912. * @param[in] ulStoreId The id of the store containing the message.
  913. * @param[in] lpsGuid Seems to be unused.
  914. * @param[in] ulFlags Flags (SYNC_BEST_BODY to stream the best body for the message,
  915. * SYNC_LIMITED_IMESSAGE to only stream the plain text body).
  916. * @param[in] lpSink Pointer to an ECSerializer instance to which the serialized
  917. * data should be written.
  918. * @param[in] bTop Specifies that this is a toplevel message. Must be true excep
  919. * when called by SerializeMessage itself.
  920. */
  921. ECRESULT SerializeMessage(ECSession *lpecSession, ECDatabase *lpStreamDatabase, ECAttachmentStorage *lpAttachmentStorage, LPCSTREAMCAPS lpStreamCaps, unsigned int ulObjId, unsigned int ulObjType, unsigned int ulStoreId, GUID *lpsGuid, ULONG ulFlags, ECSerializer *lpSink, bool bTop)
  922. {
  923. ECRESULT er = erSuccess;
  924. unsigned int ulStreamVersion = STREAM_VERSION;
  925. unsigned int ulSubObjId = 0;
  926. unsigned int ulSubObjType = 0;
  927. unsigned int ulCount = 0;
  928. ChildPropsMap mapChildProps;
  929. NamedPropDefMap mapNamedPropDefs;
  930. DB_ROW lpDBRow = NULL;
  931. DB_LENGTHS lpDBLen = NULL;
  932. DB_RESULT lpDBResult, lpDBResultAttachment;
  933. std::string strQuery;
  934. bool bUseSQLMulti = parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_sql_procedures"));
  935. if (ulObjType != MAPI_MESSAGE) {
  936. er = KCERR_NO_SUPPORT;
  937. goto exit;
  938. }
  939. if (lpStreamCaps == NULL) {
  940. lpStreamCaps = STREAM_CAPS_CURRENT; // Set to current stream capabilities.
  941. if ((lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE) == 0) {
  942. ulStreamVersion = 0;
  943. lpStreamCaps = &g_StreamCaps[0];
  944. }
  945. er = lpSink->Write(&ulStreamVersion, sizeof(ulStreamVersion), 1);
  946. if (er != erSuccess)
  947. goto exit;
  948. }
  949. // szGetProps
  950. er = SerializeProps(lpecSession, lpStreamDatabase, lpAttachmentStorage, lpStreamCaps, ulObjId, ulObjType, ulStoreId, lpsGuid, ulFlags, lpSink, bTop);
  951. if (er != erSuccess)
  952. goto exit;
  953. // szPrepareGetProps
  954. er = PrepareReadProps(NULL, lpStreamDatabase, !bUseSQLMulti, true, 0, ulObjId, 0, &mapChildProps, &mapNamedPropDefs);
  955. if (er != erSuccess)
  956. goto exit;
  957. if (bUseSQLMulti) {
  958. // Serialize sub objects
  959. er = lpStreamDatabase->GetNextResult(&lpDBResult);
  960. } else {
  961. // begin of loop part
  962. strQuery = "SELECT id,hierarchy.type FROM hierarchy WHERE parent=" + stringify(ulObjId);
  963. er = lpStreamDatabase->DoSelect(strQuery, &lpDBResult);
  964. }
  965. if (er != erSuccess)
  966. goto exit;
  967. ulCount = lpStreamDatabase->GetNumRows(lpDBResult);
  968. er = lpSink->Write(&ulCount, sizeof(ulCount), 1);
  969. if (er != erSuccess)
  970. goto exit;
  971. for (unsigned i = 0; i < ulCount; ++i) {
  972. lpDBRow = lpStreamDatabase->FetchRow(lpDBResult);
  973. lpDBLen = lpStreamDatabase->FetchRowLengths(lpDBResult);
  974. if (lpDBRow == NULL || lpDBLen == NULL) {
  975. er = KCERR_DATABASE_ERROR;
  976. ec_log_err("SerializeMessage(): fetchrow/fetchrowlengths failed");
  977. goto exit;
  978. }
  979. // ulSubObjType should be MAPI_MAILUSER, MAPI_DISTLIST or MAPI_ATTACH. But in reality
  980. // it can be anything. We'll send the 'wrong' type so the receiver can also return
  981. // the wrong type that might be expected by a client.
  982. ulSubObjType = atoi(lpDBRow[1]);
  983. er = lpSink->Write(&ulSubObjType, sizeof(ulSubObjType), 1);
  984. if (er != erSuccess)
  985. goto exit;
  986. ulSubObjId = atoi(lpDBRow[0]);
  987. er = lpSink->Write(&ulSubObjId, sizeof(ulSubObjId), 1);
  988. if (er != erSuccess)
  989. goto exit;
  990. // Output properties for this object
  991. auto iterChild = mapChildProps.find(ulSubObjId);
  992. if (iterChild != mapChildProps.cend()) {
  993. struct propValArray props;
  994. iterChild->second.lpPropVals->GetPropValArray(&props);
  995. er = SerializeProps(&props, lpStreamCaps, lpSink, &mapNamedPropDefs);
  996. FreePropValArray(&props, false);
  997. if(er != erSuccess)
  998. goto exit;
  999. }
  1000. if(ulSubObjType == MAPI_ATTACH) {
  1001. unsigned int ulLen = 0;
  1002. if (lpAttachmentStorage->ExistAttachment(ulSubObjId, PROP_ID(PR_ATTACH_DATA_BIN))) {
  1003. unsigned char *data = NULL;
  1004. size_t temp = 0;
  1005. er = lpAttachmentStorage->LoadAttachment(NULL, ulSubObjId, PROP_ID(PR_ATTACH_DATA_BIN), &temp, &data);
  1006. if (er != erSuccess)
  1007. goto exit;
  1008. ulLen = (unsigned int)temp;
  1009. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  1010. if (er != erSuccess) {
  1011. s_free(NULL, data);
  1012. goto exit;
  1013. }
  1014. er = lpSink->Write(data, 1, ulLen);
  1015. s_free(NULL, data);
  1016. if (er != erSuccess)
  1017. goto exit;
  1018. } else {
  1019. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  1020. if (er != erSuccess)
  1021. goto exit;
  1022. }
  1023. // start sub objects, can only be 0 or 1
  1024. if (bUseSQLMulti) {
  1025. er = lpStreamDatabase->GetNextResult(&lpDBResultAttachment);
  1026. } else {
  1027. strQuery = "SELECT id, hierarchy.type FROM hierarchy WHERE parent = " + stringify(ulSubObjId) + " LIMIT 1";
  1028. er = lpStreamDatabase->DoSelect(strQuery, &lpDBResultAttachment);
  1029. }
  1030. if (er != erSuccess)
  1031. goto exit;
  1032. ulLen = lpStreamDatabase->GetNumRows(lpDBResultAttachment) >= 1 ? 1 : 0; // Force value to 0 or 1, we cannot output more than one submessage.
  1033. er = lpSink->Write(&ulLen, sizeof(ulLen), 1);
  1034. if (er != erSuccess)
  1035. goto exit;
  1036. lpDBRow = lpStreamDatabase->FetchRow(lpDBResultAttachment);
  1037. if(lpDBRow != NULL) {
  1038. if(lpDBRow[0] == NULL) {
  1039. er = KCERR_DATABASE_ERROR;
  1040. ec_log_err("SerializeMessage(): column null");
  1041. goto exit;
  1042. }
  1043. ulSubObjType = atoi(lpDBRow[1]);
  1044. er = lpSink->Write(&ulSubObjType, sizeof(ulSubObjType), 1);
  1045. if (er != erSuccess)
  1046. goto exit;
  1047. ulSubObjId = atoi(lpDBRow[0]);
  1048. er = lpSink->Write(&ulSubObjId, sizeof(ulSubObjId), 1);
  1049. if (er != erSuccess)
  1050. goto exit;
  1051. // Recurse into subobject, depth is ignored when not using sql procedures
  1052. er = SerializeMessage(lpecSession, lpStreamDatabase, lpAttachmentStorage, lpStreamCaps, ulSubObjId, ulSubObjType, ulStoreId, lpsGuid, ulFlags, lpSink, false);
  1053. if (er != erSuccess)
  1054. goto exit;
  1055. }
  1056. }
  1057. }
  1058. if(bTop && bUseSQLMulti)
  1059. lpStreamDatabase->FinalizeMulti();
  1060. exit:
  1061. if (er != erSuccess)
  1062. ec_log_err("SerializeObject failed with error code 0x%08x for object %d", er, ulObjId );
  1063. FreeChildProps(&mapChildProps);
  1064. return er;
  1065. }
  1066. static ECRESULT DeserializePropVal(struct soap *soap,
  1067. LPCSTREAMCAPS lpStreamCaps, NamedPropertyMapper &namedPropertyMapper,
  1068. propVal **lppsPropval, ECSerializer *lpSource)
  1069. {
  1070. ECRESULT er;
  1071. gsoap_size_t ulCount;
  1072. unsigned int ulLen;
  1073. propVal *lpsPropval = NULL;
  1074. unsigned char b;
  1075. GUID guid = {0};
  1076. unsigned int ulKind = 0;
  1077. unsigned int ulNameId = 0;
  1078. std::string strNameString;
  1079. unsigned int ulLocalId = 0;
  1080. convert_context converter;
  1081. lpsPropval = s_alloc<propVal>(soap);
  1082. er = lpSource->Read(&lpsPropval->ulPropTag, sizeof(lpsPropval->ulPropTag), 1);
  1083. if (er != erSuccess)
  1084. return er;
  1085. switch (PROP_TYPE(lpsPropval->ulPropTag)) {
  1086. case PT_I2:
  1087. lpsPropval->__union = SOAP_UNION_propValData_i;
  1088. er = lpSource->Read(&lpsPropval->Value.i, sizeof(lpsPropval->Value.i), 1);
  1089. break;
  1090. case PT_LONG:
  1091. lpsPropval->__union = SOAP_UNION_propValData_ul;
  1092. er = lpSource->Read(&lpsPropval->Value.ul, sizeof(lpsPropval->Value.ul), 1);
  1093. break;
  1094. case PT_R4:
  1095. lpsPropval->__union = SOAP_UNION_propValData_flt;
  1096. er = lpSource->Read(&lpsPropval->Value.flt, sizeof(lpsPropval->Value.flt), 1);
  1097. break;
  1098. case PT_BOOLEAN:
  1099. lpsPropval->__union = SOAP_UNION_propValData_b;
  1100. er = lpSource->Read(&b, sizeof(b), 1);
  1101. lpsPropval->Value.b = (b != 0);
  1102. break;
  1103. case PT_DOUBLE:
  1104. case PT_APPTIME:
  1105. lpsPropval->__union = SOAP_UNION_propValData_dbl;
  1106. er = lpSource->Read(&lpsPropval->Value.dbl, sizeof(lpsPropval->Value.dbl), 1);
  1107. break;
  1108. case PT_CURRENCY:
  1109. case PT_SYSTIME:
  1110. lpsPropval->__union = SOAP_UNION_propValData_hilo;
  1111. lpsPropval->Value.hilo = s_alloc<hiloLong>(soap);
  1112. er = lpSource->Read(&lpsPropval->Value.hilo->hi, sizeof(lpsPropval->Value.hilo->hi), 1);
  1113. if (er == erSuccess)
  1114. er = lpSource->Read(&lpsPropval->Value.hilo->lo, sizeof(lpsPropval->Value.hilo->lo), 1);
  1115. break;
  1116. case PT_I8:
  1117. lpsPropval->__union = SOAP_UNION_propValData_li;
  1118. er = lpSource->Read(&lpsPropval->Value.li, sizeof(lpsPropval->Value.li), 1);
  1119. break;
  1120. case PT_STRING8:
  1121. case PT_UNICODE:
  1122. lpsPropval->__union = SOAP_UNION_propValData_lpszA;
  1123. er = lpSource->Read(&ulLen, sizeof(ulLen), 1);
  1124. if (er == erSuccess) {
  1125. if (lpStreamCaps->bSupportUnicode) {
  1126. lpsPropval->Value.lpszA = s_alloc<char>(soap, ulLen + 1);
  1127. memset(lpsPropval->Value.lpszA, 0, ulLen + 1);
  1128. er = lpSource->Read(lpsPropval->Value.lpszA, 1, ulLen);
  1129. } else {
  1130. std::string strData(ulLen, '\0');
  1131. er = lpSource->Read((void*)strData.data(), 1, ulLen);
  1132. if (er == erSuccess) {
  1133. const utf8string strConverted = converter.convert_to<utf8string>(strData, rawsize(strData), "WINDOWS-1252");
  1134. lpsPropval->Value.lpszA = s_strcpy(soap, strConverted.c_str());
  1135. }
  1136. }
  1137. }
  1138. break;
  1139. case PT_CLSID:
  1140. case PT_BINARY:
  1141. lpsPropval->__union = SOAP_UNION_propValData_bin;
  1142. er = lpSource->Read(&ulLen, sizeof(ulLen), 1);
  1143. if (er == erSuccess) {
  1144. lpsPropval->Value.bin = s_alloc<xsd__base64Binary>(soap);
  1145. lpsPropval->Value.bin->__size = ulLen;
  1146. lpsPropval->Value.bin->__ptr = s_alloc<unsigned char>(soap, ulLen);
  1147. er = lpSource->Read(lpsPropval->Value.bin->__ptr, 1, ulLen);
  1148. }
  1149. break;
  1150. case PT_MV_I2:
  1151. lpsPropval->__union = SOAP_UNION_propValData_mvi;
  1152. er = lpSource->Read(&ulCount, sizeof(ulCount), 1);
  1153. if (er == erSuccess) {
  1154. lpsPropval->Value.mvi.__size = ulCount;
  1155. lpsPropval->Value.mvi.__ptr = s_alloc<short>(soap, ulCount);
  1156. er = lpSource->Read(lpsPropval->Value.mvi.__ptr, sizeof *lpsPropval->Value.mvi.__ptr, ulCount);
  1157. }
  1158. break;
  1159. case PT_MV_LONG:
  1160. lpsPropval->__union = SOAP_UNION_propValData_mvl;
  1161. er = lpSource->Read(&ulCount, sizeof(ulCount), 1);
  1162. if (er == erSuccess) {
  1163. lpsPropval->Value.mvl.__size = ulCount;
  1164. lpsPropval->Value.mvl.__ptr = s_alloc<unsigned int>(soap, ulCount);
  1165. er = lpSource->Read(lpsPropval->Value.mvl.__ptr, sizeof *lpsPropval->Value.mvl.__ptr, ulCount);
  1166. }
  1167. break;
  1168. case PT_MV_R4:
  1169. lpsPropval->__union = SOAP_UNION_propValData_mvflt;
  1170. er = lpSource->Read(&ulCount, sizeof(ulCount), 1);
  1171. if (er == erSuccess) {
  1172. lpsPropval->Value.mvflt.__size = ulCount;
  1173. lpsPropval->Value.mvflt.__ptr = s_alloc<float>(soap, ulCount);
  1174. er = lpSource->Read(lpsPropval->Value.mvflt.__ptr, sizeof *lpsPropval->Value.mvflt.__ptr, ulCount);
  1175. }
  1176. break;
  1177. case PT_MV_DOUBLE:
  1178. case PT_MV_APPTIME:
  1179. lpsPropval->__union = SOAP_UNION_propValData_mvdbl;
  1180. er = lpSource->Read(&ulCount, sizeof(ulCount), 1);
  1181. if (er == erSuccess) {
  1182. lpsPropval->Value.mvdbl.__size = ulCount;
  1183. lpsPropval->Value.mvdbl.__ptr = s_alloc<double>(soap, ulCount);
  1184. er = lpSource->Read(lpsPropval->Value.mvdbl.__ptr, sizeof *lpsPropval->Value.mvdbl.__ptr, ulCount);
  1185. }
  1186. break;
  1187. case PT_MV_CURRENCY:
  1188. case PT_MV_SYSTIME:
  1189. lpsPropval->__union = SOAP_UNION_propValData_mvhilo;
  1190. er = lpSource->Read(&ulCount, sizeof(ulCount), 1);
  1191. if (er == erSuccess) {
  1192. lpsPropval->Value.mvhilo.__size = ulCount;
  1193. lpsPropval->Value.mvhilo.__ptr = s_alloc<hiloLong>(soap, ulCount);
  1194. for (gsoap_size_t x = 0; er == erSuccess && x < ulCount; ++x) {
  1195. er = lpSource->Read(&lpsPropval->Value.mvhilo.__ptr[x].hi, sizeof(lpsPropval->Value.mvhilo.__ptr[x].hi), ulCount);
  1196. if (er == erSuccess)
  1197. er = lpSource->Read(&lpsPropval->Value.mvhilo.__ptr[x].lo, sizeof(lpsPropval->Value.mvhilo.__ptr[x].lo), ulCount);
  1198. }
  1199. }
  1200. break;
  1201. case PT_MV_BINARY:
  1202. case PT_MV_CLSID:
  1203. lpsPropval->__union = SOAP_UNION_propValData_mvbin;
  1204. er = lpSource->Read(&ulCount, sizeof(ulCount), 1);
  1205. if (er == erSuccess) {
  1206. lpsPropval->Value.mvbin.__size = ulCount;
  1207. lpsPropval->Value.mvbin.__ptr = s_alloc<xsd__base64Binary>(soap, ulCount);
  1208. for (gsoap_size_t x = 0; er == erSuccess && x < ulCount; ++x) {
  1209. er = lpSource->Read(&ulLen, sizeof(ulLen), 1);
  1210. if (er == erSuccess) {
  1211. lpsPropval->Value.mvbin.__ptr[x].__size = ulLen;
  1212. lpsPropval->Value.mvbin.__ptr[x].__ptr = s_alloc<unsigned char>(soap, ulLen);
  1213. er = lpSource->Read(lpsPropval->Value.mvbin.__ptr[x].__ptr, 1, ulLen);
  1214. }
  1215. }
  1216. }
  1217. break;
  1218. case PT_MV_STRING8:
  1219. case PT_MV_UNICODE:
  1220. lpsPropval->__union = SOAP_UNION_propValData_mvszA;
  1221. er = lpSource->Read(&ulCount, sizeof(ulCount), 1);
  1222. if (er == erSuccess) {
  1223. lpsPropval->Value.mvszA.__size = ulCount;
  1224. lpsPropval->Value.mvszA.__ptr = s_alloc<char*>(soap, ulCount);
  1225. for (gsoap_size_t x = 0; er == erSuccess && x < ulCount; ++x) {
  1226. er = lpSource->Read(&ulLen, sizeof(ulLen), 1);
  1227. if (er == erSuccess) {
  1228. if (lpStreamCaps->bSupportUnicode) {
  1229. lpsPropval->Value.mvszA.__ptr[x] = s_alloc<char>(soap, ulLen + 1);
  1230. memset(lpsPropval->Value.mvszA.__ptr[x], 0, ulLen + 1);
  1231. er = lpSource->Read(lpsPropval->Value.mvszA.__ptr[x], 1, ulLen);
  1232. } else {
  1233. std::string strData(ulLen, '\0');
  1234. er = lpSource->Read((void*)strData.data(), 1, ulLen);
  1235. if (er == erSuccess) {
  1236. const utf8string strConverted = converter.convert_to<utf8string>(strData, rawsize(strData), "WINDOWS-1252");
  1237. lpsPropval->Value.mvszA.__ptr[x] = s_strcpy(soap, strConverted.c_str());
  1238. }
  1239. }
  1240. }
  1241. }
  1242. }
  1243. break;
  1244. case PT_MV_I8:
  1245. lpsPropval->__union = SOAP_UNION_propValData_mvli;
  1246. er = lpSource->Read(&ulCount, sizeof(ulCount), 1);
  1247. if (er == erSuccess) {
  1248. lpsPropval->Value.mvli.__size = ulCount;
  1249. lpsPropval->Value.mvli.__ptr = s_alloc<LONG64>(soap, ulCount);
  1250. er = lpSource->Read(lpsPropval->Value.mvli.__ptr, sizeof *lpsPropval->Value.mvli.__ptr, ulCount);
  1251. }
  1252. break;
  1253. default:
  1254. return KCERR_INVALID_TYPE;
  1255. }
  1256. // If the proptag is in the dynamic named property range, we need to get the correct local proptag
  1257. if (PROP_ID(lpsPropval->ulPropTag) > 0x8500) {
  1258. er = lpSource->Read(&guid, 1, sizeof(guid));
  1259. if (er == erSuccess)
  1260. er = lpSource->Read(&ulKind, sizeof(ulKind), 1);
  1261. if (er == erSuccess && ulKind == MNID_ID) {
  1262. er = lpSource->Read(&ulNameId, sizeof(ulNameId), 1);
  1263. if (er == erSuccess)
  1264. er = namedPropertyMapper.GetId(guid, ulNameId, &ulLocalId);
  1265. } else if (er == erSuccess && ulKind == MNID_STRING) {
  1266. er = lpSource->Read(&ulLen, sizeof(ulLen), 1);
  1267. if (er == erSuccess) {
  1268. strNameString.resize(ulLen, 0);
  1269. er = lpSource->Read((void*)strNameString.data(), 1, ulLen);
  1270. }
  1271. if (er == erSuccess)
  1272. er = namedPropertyMapper.GetId(guid, strNameString, &ulLocalId);
  1273. }
  1274. if (er == erSuccess)
  1275. lpsPropval->ulPropTag = PROP_TAG(PROP_TYPE(lpsPropval->ulPropTag), ulLocalId);
  1276. }
  1277. *lppsPropval = lpsPropval;
  1278. return er;
  1279. }
  1280. static ECRESULT DeserializeProps(ECSession *lpecSession, ECDatabase *lpDatabase,
  1281. ECAttachmentStorage *lpAttachmentStorage, const StreamCaps *lpStreamCaps,
  1282. unsigned int ulObjId, unsigned int ulObjType, unsigned int ulStoreId,
  1283. GUID *lpsGuid, bool bNewItem, ECSerializer *lpSource,
  1284. struct propValArray **lppPropValArray)
  1285. {
  1286. ECRESULT er = erSuccess;
  1287. unsigned int ulCount = 0;
  1288. unsigned int ulFlags = 0;
  1289. unsigned int ulParentId = 0;
  1290. unsigned int ulOwner = 0;
  1291. unsigned int ulParentType = 0;
  1292. gsoap_size_t nMVItems = 0;
  1293. unsigned int ulAffected = 0;
  1294. unsigned int ulLen = 0;
  1295. propVal *lpsPropval = NULL;
  1296. struct soap *soap = NULL;
  1297. struct propValArray *lpPropValArray = NULL;
  1298. NamedPropertyMapper namedPropertyMapper(lpDatabase);
  1299. std::string strQuery;
  1300. std::string strInsertQuery;
  1301. std::string strInsertTProp;
  1302. std::string strColData;
  1303. std::string strColName;
  1304. SOURCEKEY sSourceKey;
  1305. DB_RESULT lpDBResult;
  1306. DB_ROW lpDBRow = NULL;
  1307. std::set<unsigned int> setInserted;
  1308. if (!lpDatabase) {
  1309. er = KCERR_DATABASE_ERROR;
  1310. goto exit;
  1311. }
  1312. if (!lpAttachmentStorage) {
  1313. er = KCERR_INVALID_PARAMETER;
  1314. goto exit;
  1315. }
  1316. er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, &ulParentId, &ulOwner, &ulFlags, NULL);
  1317. if (er != erSuccess)
  1318. goto exit;
  1319. er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentId, NULL, NULL, NULL, &ulParentType);
  1320. if (er != erSuccess)
  1321. goto exit;
  1322. // Get the number of properties
  1323. er = lpSource->Read(&ulCount, sizeof(ulCount), 1);
  1324. if (er != erSuccess)
  1325. goto exit;
  1326. if (lppPropValArray) {
  1327. // If requested we can store upto ulCount properties. Currently we won't store them all though.
  1328. // Note that we test on lppPropValArray but allocate lpPropValArray. We'll assign that to
  1329. // *lppPropValArray later if all went well.
  1330. lpPropValArray = s_alloc<struct propValArray>(NULL);
  1331. lpPropValArray->__ptr = s_alloc<struct propVal>(NULL, ulCount);
  1332. memset(lpPropValArray->__ptr, 0, sizeof(struct propVal) * ulCount);
  1333. lpPropValArray->__size = 0;
  1334. }
  1335. for (unsigned i = 0; i < ulCount; ++i) {
  1336. // We'll (ab)use a soap structure as a memory pool.
  1337. assert(soap == NULL);
  1338. soap = soap_new();
  1339. er = DeserializePropVal(soap, lpStreamCaps, namedPropertyMapper, &lpsPropval, lpSource);
  1340. if (er != erSuccess)
  1341. goto exit;
  1342. auto iterInserted = setInserted.find(lpsPropval->ulPropTag);
  1343. if (iterInserted != setInserted.cend())
  1344. goto next_property;
  1345. if (ECGenProps::IsPropRedundant(lpsPropval->ulPropTag, ulObjType) == erSuccess)
  1346. goto next_property;
  1347. // Same goes for flags in PR_MESSAGE_FLAGS
  1348. if (lpsPropval->ulPropTag == PR_MESSAGE_FLAGS) {
  1349. // ulFlags is obtained from the hierarchy table, which should only contain
  1350. // 'unsettable' flags
  1351. assert((ulFlags & ~MSGFLAG_UNSETTABLE) == 0);
  1352. // Normalize PR_MESSAGE_FLAGS so that the user cannot change flags that are also
  1353. // stored in the hierarchy table.
  1354. lpsPropval->Value.ul = (lpsPropval->Value.ul & ~MSGFLAG_UNSETTABLE) | ulFlags;
  1355. if (lpPropValArray)
  1356. CopyPropVal(lpsPropval, lpPropValArray->__ptr + lpPropValArray->__size++);
  1357. }
  1358. // Make sure we dont have a colliding PR_SOURCE_KEY. This can happen if a user imports an exported message for example.
  1359. if (lpsPropval->ulPropTag == PR_SOURCE_KEY) {
  1360. // don't use the sourcekey if found.
  1361. // Don't query the cache as that can be out of sync with the db in rare occasions.
  1362. strQuery =
  1363. "SELECT hierarchyid FROM indexedproperties "
  1364. "WHERE tag=" + stringify(PROP_ID(PR_SOURCE_KEY)) +
  1365. " AND val_binary=" + lpDatabase->EscapeBinary(lpsPropval->Value.bin->__ptr, lpsPropval->Value.bin->__size);
  1366. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  1367. if(er != erSuccess)
  1368. goto exit;
  1369. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  1370. // We can't use lpDBRow here except for checking if it was NULL.
  1371. if (lpDBRow != NULL)
  1372. continue;
  1373. strQuery = "REPLACE INTO indexedproperties(hierarchyid,tag,val_binary) VALUES (" + stringify(ulObjId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(lpsPropval->Value.bin->__ptr, lpsPropval->Value.bin->__size) + ")";
  1374. er = lpDatabase->DoInsert(strQuery);
  1375. if (er != erSuccess)
  1376. goto exit;
  1377. setInserted.insert(lpsPropval->ulPropTag);
  1378. g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY), lpsPropval->Value.bin->__size, lpsPropval->Value.bin->__ptr, ulObjId);
  1379. goto next_property;
  1380. }
  1381. if (PROP_TYPE(lpsPropval->ulPropTag) & MV_FLAG) {
  1382. nMVItems = GetMVItemCount(lpsPropval);
  1383. for (gsoap_size_t j = 0; j < nMVItems; ++j) {
  1384. er = CopySOAPPropValToDatabaseMVPropVal(lpsPropval, j, strColName, strColData, lpDatabase);
  1385. if (er != erSuccess) {
  1386. er = erSuccess;
  1387. goto next_property;
  1388. }
  1389. strQuery = "REPLACE INTO mvproperties(hierarchyid,orderid,tag,type," + strColName + ") VALUES(" + stringify(ulObjId) + "," + stringify(j) + "," + stringify(PROP_ID(lpsPropval->ulPropTag)) + "," + stringify(PROP_TYPE(lpsPropval->ulPropTag)) + "," + strColData + ")";
  1390. er = lpDatabase->DoInsert(strQuery, NULL, &ulAffected);
  1391. if (er != erSuccess)
  1392. goto exit;
  1393. if (ulAffected != 1) {
  1394. er = KCERR_DATABASE_ERROR;
  1395. ec_log_err("DeserializeProps(): Unexpected affected row count");
  1396. goto exit;
  1397. }
  1398. }
  1399. } else {
  1400. // Write the property to the database
  1401. er = WriteSingleProp(lpDatabase, ulObjId, ulParentId, lpsPropval, false, lpDatabase->GetMaxAllowedPacket(), strInsertQuery);
  1402. if (er == KCERR_TOO_BIG) {
  1403. er = lpDatabase->DoInsert(strInsertQuery);
  1404. if (er == erSuccess) {
  1405. strInsertQuery.clear();
  1406. er = WriteSingleProp(lpDatabase, ulObjId, ulParentId, lpsPropval, false, lpDatabase->GetMaxAllowedPacket(), strInsertQuery);
  1407. }
  1408. }
  1409. if (er != erSuccess)
  1410. goto exit;
  1411. // Write the property to the table properties if needed (only on objects in folders (folders, messages), and if the property is being tracked here.
  1412. if (ulParentType == MAPI_FOLDER) {
  1413. // Cache the written value
  1414. sObjectTableKey key(ulObjId, 0);
  1415. g_lpSessionManager->GetCacheManager()->SetCell(&key, lpsPropval->ulPropTag, lpsPropval);
  1416. if (0) {
  1417. // FIXME do we need this code? Currently we get always a deferredupdate!
  1418. // Please also update cmd.cpp:WriteProps
  1419. er = WriteSingleProp(lpDatabase, ulObjId, ulParentId, lpsPropval, true, lpDatabase->GetMaxAllowedPacket(), strInsertTProp);
  1420. if (er == KCERR_TOO_BIG) {
  1421. er = lpDatabase->DoInsert(strInsertTProp);
  1422. if (er == erSuccess) {
  1423. strInsertTProp.clear();
  1424. er = WriteSingleProp(lpDatabase, ulObjId, ulParentId, lpsPropval, true, lpDatabase->GetMaxAllowedPacket(), strInsertTProp);
  1425. }
  1426. }
  1427. if(er != erSuccess)
  1428. goto exit;
  1429. }
  1430. }
  1431. }
  1432. setInserted.insert(lpsPropval->ulPropTag);
  1433. next_property:
  1434. soap_destroy(soap);
  1435. soap_end(soap);
  1436. soap_free(soap);
  1437. soap = NULL;
  1438. }
  1439. if (!strInsertQuery.empty()) {
  1440. er = lpDatabase->DoInsert(strInsertQuery);
  1441. if (er != erSuccess)
  1442. goto exit;
  1443. }
  1444. if(ulParentType == MAPI_FOLDER) {
  1445. if (0) {
  1446. /* Modification, just directly write the tproperties
  1447. * The idea behind this is that we'd need some serious random-access reads to properties later when flushing
  1448. * tproperties, and we have the properties in memory now anyway. Also, modifications usually are just a few properties, causing
  1449. * only minor random I/O on tproperties, and a tproperties flush reads all the properties, not just the modified ones.
  1450. */
  1451. if (!strInsertTProp.empty()) {
  1452. er = lpDatabase->DoInsert(strInsertTProp);
  1453. if (er != erSuccess)
  1454. goto exit;
  1455. }
  1456. } else {
  1457. // Instead of writing directly to tproperties, save a delayed write request (flushed on table open).
  1458. if (ulParentId != CACHE_NO_PARENT) {
  1459. er = ECTPropsPurge::AddDeferredUpdateNoPurge(lpDatabase, ulParentId, 0, ulObjId);
  1460. if(er != erSuccess)
  1461. goto exit;
  1462. }
  1463. }
  1464. }
  1465. if (bNewItem && ulParentType == MAPI_FOLDER && RealObjType(ulObjType, ulParentType) == MAPI_MESSAGE) {
  1466. auto iterInserted = setInserted.find(PR_SOURCE_KEY);
  1467. if (iterInserted == setInserted.cend()) {
  1468. er = lpecSession->GetNewSourceKey(&sSourceKey);
  1469. if (er != erSuccess)
  1470. goto exit;
  1471. strQuery = "INSERT INTO indexedproperties(hierarchyid,tag,val_binary) VALUES(" + stringify(ulObjId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + ")";
  1472. er = lpDatabase->DoInsert(strQuery);
  1473. if (er != erSuccess)
  1474. goto exit;
  1475. g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY), sSourceKey.size(), sSourceKey, ulObjId);
  1476. }
  1477. }
  1478. if (RealObjType(ulObjType, ulParentType) == MAPI_ATTACH) {
  1479. er = lpSource->Read(&ulLen, sizeof(ulLen), 1);
  1480. if (er != erSuccess)
  1481. goto exit;
  1482. // We don't require the instance id, since we have no way of returning the instance id of this new object to the client.
  1483. er = lpAttachmentStorage->SaveAttachment(ulObjId, PROP_ID(PR_ATTACH_DATA_BIN), true, ulLen, lpSource, NULL);
  1484. if (er != erSuccess)
  1485. goto exit;
  1486. }
  1487. if (!bNewItem)
  1488. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulObjId);
  1489. g_lpSessionManager->GetCacheManager()->SetObject(ulObjId, ulParentId, ulOwner, ulFlags, ulObjType);
  1490. if (lpPropValArray) {
  1491. assert(lppPropValArray != NULL);
  1492. *lppPropValArray = lpPropValArray;
  1493. lpPropValArray = NULL;
  1494. }
  1495. exit:
  1496. if (lpPropValArray)
  1497. FreePropValArray(lpPropValArray, true);
  1498. if (soap) {
  1499. soap_destroy(soap);
  1500. soap_end(soap);
  1501. soap_free(soap);
  1502. }
  1503. return er;
  1504. }
  1505. ECRESULT DeserializeObject(ECSession *lpecSession, ECDatabase *lpDatabase, ECAttachmentStorage *lpAttachmentStorage, LPCSTREAMCAPS lpStreamCaps, unsigned int ulObjId, unsigned int ulStoreId, GUID *lpsGuid, bool bNewItem, unsigned long long ullIMAP, ECSerializer *lpSource, struct propValArray **lppPropValArray)
  1506. {
  1507. ECRESULT er = erSuccess;
  1508. unsigned int ulStreamVersion = 0;
  1509. unsigned int ulObjType = 0;
  1510. unsigned int ulRealObjType = 0;
  1511. unsigned int ulParentId = 0;
  1512. unsigned int ulParentType = 0;
  1513. unsigned int ulSize =0 ;
  1514. struct propValArray *lpPropValArray = NULL;
  1515. std::string strQuery;
  1516. if (!lpDatabase) {
  1517. er = KCERR_DATABASE_ERROR;
  1518. goto exit;
  1519. }
  1520. er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, &ulParentId, NULL, NULL, &ulObjType);
  1521. if (er != erSuccess)
  1522. goto exit;
  1523. er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentId, NULL, NULL, NULL, &ulParentType);
  1524. if (er != erSuccess)
  1525. goto exit;
  1526. // Normalize the object type, but keep the original for storing in the db
  1527. ulRealObjType = RealObjType(ulObjType, ulParentType);
  1528. if (ulRealObjType != MAPI_MESSAGE && ulRealObjType != MAPI_ATTACH && ulRealObjType != MAPI_MAILUSER && ulRealObjType != MAPI_DISTLIST) {
  1529. er = KCERR_NO_SUPPORT;
  1530. goto exit;
  1531. }
  1532. if (lpStreamCaps == NULL) {
  1533. er = lpSource->Read(&ulStreamVersion, sizeof(ulStreamVersion), 1);
  1534. if (er != erSuccess)
  1535. goto exit;
  1536. if (ulStreamVersion >= ARRAY_SIZE(g_StreamCaps)) {
  1537. er = KCERR_NO_SUPPORT;
  1538. goto exit;
  1539. }
  1540. lpStreamCaps = &g_StreamCaps[ulStreamVersion];
  1541. }
  1542. er = DeserializeProps(lpecSession, lpDatabase, lpAttachmentStorage, lpStreamCaps, ulObjId, ulObjType, ulStoreId, lpsGuid, bNewItem, lpSource, lppPropValArray ? &lpPropValArray : NULL);
  1543. if (er != erSuccess)
  1544. goto exit;
  1545. if (ulParentType == MAPI_FOLDER) {
  1546. sObjectTableKey key(ulObjId, 0);
  1547. propVal sProp;
  1548. if (bNewItem) {
  1549. er = g_lpSessionManager->GetNewSequence(ECSessionManager::SEQ_IMAP, &ullIMAP);
  1550. if (er != erSuccess)
  1551. goto exit;
  1552. }
  1553. strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_ulong) VALUES(" +
  1554. stringify(ulObjId) + "," +
  1555. stringify(PROP_ID(PR_EC_IMAP_ID)) + "," +
  1556. stringify(PROP_TYPE(PR_EC_IMAP_ID)) + "," +
  1557. stringify_int64(ullIMAP) + ")";
  1558. er = lpDatabase->DoInsert(strQuery);
  1559. if (er != erSuccess)
  1560. goto exit;
  1561. sProp.ulPropTag = PR_EC_IMAP_ID;
  1562. sProp.Value.ul = (unsigned int)ullIMAP;
  1563. sProp.__union = SOAP_UNION_propValData_ul;
  1564. er = g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_EC_IMAP_ID, &sProp);
  1565. if (er != erSuccess)
  1566. goto exit;
  1567. }
  1568. if (ulRealObjType == MAPI_MESSAGE || ulRealObjType == MAPI_ATTACH) {
  1569. unsigned int ulSubObjCount = 0;
  1570. unsigned int ulSubObjId = 0;
  1571. unsigned int ulSubObjType = 0;
  1572. BOOL fHasAttach = FALSE;
  1573. er = lpSource->Read(&ulSubObjCount, sizeof(ulSubObjCount), 1);
  1574. if (er != erSuccess)
  1575. goto exit;
  1576. for (unsigned i = 0; i < ulSubObjCount; ++i) {
  1577. er = lpSource->Read(&ulSubObjType, sizeof(ulSubObjType), 1);
  1578. if (er != erSuccess)
  1579. goto exit;
  1580. if (RealObjType(ulSubObjType, ulRealObjType) == MAPI_ATTACH)
  1581. fHasAttach = TRUE;
  1582. er = lpSource->Read(&ulSubObjId, sizeof(ulSubObjId), 1);
  1583. if (er != erSuccess)
  1584. goto exit;
  1585. /**
  1586. * For new items we're not interested in the ulSubObjId from the stream, we do need
  1587. * to create the object with the current object as its parent
  1588. **/
  1589. er = CreateObject(lpecSession, lpDatabase, ulObjId, ulObjType, ulSubObjType, 0, &ulSubObjId);
  1590. if (er != erSuccess)
  1591. goto exit;
  1592. er = DeserializeObject(lpecSession, lpDatabase, lpAttachmentStorage, lpStreamCaps, ulSubObjId, ulStoreId, lpsGuid, bNewItem, 0, lpSource, NULL);
  1593. if (er != erSuccess)
  1594. goto exit;
  1595. }
  1596. if (ulRealObjType == MAPI_MESSAGE) {
  1597. // We have to generate/update PR_HASATTACH
  1598. sObjectTableKey key(ulObjId, 0);
  1599. std::string strQuery;
  1600. struct propVal sPropHasAttach;
  1601. sPropHasAttach.ulPropTag = PR_HASATTACH;
  1602. sPropHasAttach.Value.b = (fHasAttach != FALSE);
  1603. sPropHasAttach.__union = SOAP_UNION_propValData_b;
  1604. // Write in properties
  1605. strQuery.clear();
  1606. WriteSingleProp(lpDatabase, ulObjId, ulParentId, &sPropHasAttach, false, 0, strQuery);
  1607. er = lpDatabase->DoInsert(strQuery);
  1608. if(er != erSuccess)
  1609. goto exit;
  1610. // Write in tproperties
  1611. strQuery.clear();
  1612. WriteSingleProp(lpDatabase, ulObjId, ulParentId, &sPropHasAttach, true, 0, strQuery);
  1613. er = lpDatabase->DoInsert(strQuery);
  1614. if(er != erSuccess)
  1615. goto exit;
  1616. // Update cache, since it may have been written before by WriteProps with a possibly wrong value
  1617. g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_HASATTACH, &sPropHasAttach);
  1618. // Update MSGFLAG_HASATTACH in the same way. We can assume PR_MESSAGE_FLAGS is already available, so we
  1619. // just do an update (instead of REPLACE INTO)
  1620. strQuery = (std::string)"UPDATE properties SET val_ulong = val_ulong " + (fHasAttach ? " | 0x10 " : " & ~0x10") + " WHERE hierarchyid = " + stringify(ulObjId) + " AND tag = " + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND type = " + stringify(PROP_TYPE(PR_MESSAGE_FLAGS));
  1621. er = lpDatabase->DoUpdate(strQuery);
  1622. if(er != erSuccess)
  1623. goto exit;
  1624. // Update cache if it's actually in the cache
  1625. if(g_lpSessionManager->GetCacheManager()->GetCell(&key, PR_MESSAGE_FLAGS, &sPropHasAttach, NULL, false) == erSuccess) {
  1626. sPropHasAttach.Value.ul &= ~MSGFLAG_HASATTACH;
  1627. sPropHasAttach.Value.ul |= fHasAttach ? MSGFLAG_HASATTACH : 0;
  1628. g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_MESSAGE_FLAGS, &sPropHasAttach);
  1629. }
  1630. }
  1631. // Calc size of object, now that all children are saved.
  1632. // Add new size
  1633. if (CalculateObjectSize(lpDatabase, ulObjId, ulObjType, &ulSize) == erSuccess) {
  1634. er = UpdateObjectSize(lpDatabase, ulObjId, ulObjType, UPDATE_SET, ulSize);
  1635. if (er != erSuccess)
  1636. goto exit;
  1637. if (ulRealObjType == MAPI_MESSAGE && ulParentType == MAPI_FOLDER) {
  1638. er = UpdateObjectSize(lpDatabase, ulStoreId, MAPI_STORE, UPDATE_ADD, ulSize);
  1639. if (er != erSuccess)
  1640. goto exit;
  1641. }
  1642. } else {
  1643. assert(false);
  1644. }
  1645. }
  1646. if (lpPropValArray) {
  1647. assert(lppPropValArray != NULL);
  1648. *lppPropValArray = lpPropValArray;
  1649. lpPropValArray = NULL;
  1650. }
  1651. exit:
  1652. if (er != erSuccess) {
  1653. lpSource->Flush(); // Flush the whole stream
  1654. ec_log_err("DeserializeObject failed with error code 0x%08x %s", er, GetMAPIErrorMessage(kcerr_to_mapierr(er, ~0U /* anything that yields UNKNOWN */)));
  1655. }
  1656. if (lpPropValArray)
  1657. FreePropValArray(lpPropValArray, true);
  1658. return er;
  1659. }
  1660. static ECRESULT GetValidatedPropType(DB_ROW lpRow, unsigned int *lpulType)
  1661. {
  1662. ECRESULT er = KCERR_DATABASE_ERROR;
  1663. unsigned int ulType = 0;
  1664. if (lpRow == NULL || lpulType == NULL)
  1665. return KCERR_INVALID_PARAMETER;
  1666. ulType = atoi(lpRow[FIELD_NR_TYPE]);
  1667. switch (ulType) {
  1668. case PT_I2:
  1669. if (lpRow[FIELD_NR_ULONG] == NULL)
  1670. return er;
  1671. break;
  1672. case PT_LONG:
  1673. if (lpRow[FIELD_NR_ULONG] == NULL)
  1674. return er;
  1675. break;
  1676. case PT_R4:
  1677. if (lpRow[FIELD_NR_DOUBLE] == NULL)
  1678. return er;
  1679. break;
  1680. case PT_BOOLEAN:
  1681. if (lpRow[FIELD_NR_ULONG] == NULL)
  1682. return er;
  1683. break;
  1684. case PT_DOUBLE:
  1685. case PT_APPTIME:
  1686. if (lpRow[FIELD_NR_DOUBLE] == NULL)
  1687. return er;
  1688. break;
  1689. case PT_CURRENCY:
  1690. case PT_SYSTIME:
  1691. if (lpRow[FIELD_NR_HI] == NULL || lpRow[FIELD_NR_LO] == NULL)
  1692. return er;
  1693. break;
  1694. case PT_I8:
  1695. if (lpRow[FIELD_NR_LONGINT] == NULL)
  1696. return er;
  1697. break;
  1698. case PT_STRING8:
  1699. case PT_UNICODE:
  1700. if (lpRow[FIELD_NR_STRING] == NULL)
  1701. return er;
  1702. break;
  1703. case PT_CLSID:
  1704. case PT_BINARY:
  1705. if (lpRow[FIELD_NR_BINARY] == NULL)
  1706. return er;
  1707. break;
  1708. case PT_MV_I2:
  1709. if (lpRow[FIELD_NR_ULONG] == NULL)
  1710. return er;
  1711. break;
  1712. case PT_MV_LONG:
  1713. if (lpRow[FIELD_NR_ULONG] == NULL)
  1714. return er;
  1715. break;
  1716. case PT_MV_R4:
  1717. if (lpRow[FIELD_NR_DOUBLE] == NULL)
  1718. return er;
  1719. break;
  1720. case PT_MV_DOUBLE:
  1721. case PT_MV_APPTIME:
  1722. if (lpRow[FIELD_NR_DOUBLE] == NULL)
  1723. return er;
  1724. break;
  1725. case PT_MV_CURRENCY:
  1726. case PT_MV_SYSTIME:
  1727. if (lpRow[FIELD_NR_HI] == NULL || lpRow[FIELD_NR_LO] == NULL)
  1728. return er;
  1729. break;
  1730. case PT_MV_BINARY:
  1731. case PT_MV_CLSID:
  1732. if (lpRow[FIELD_NR_BINARY] == NULL)
  1733. return er;
  1734. break;
  1735. case PT_MV_STRING8:
  1736. case PT_MV_UNICODE:
  1737. if (lpRow[FIELD_NR_STRING] == NULL)
  1738. return er;
  1739. break;
  1740. case PT_MV_I8:
  1741. if (lpRow[FIELD_NR_LONGINT] == NULL)
  1742. return er;
  1743. break;
  1744. default:
  1745. return KCERR_INVALID_TYPE;
  1746. }
  1747. *lpulType = ulType;
  1748. return erSuccess;
  1749. }
  1750. } /* namespace */