tnef.cpp 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931
  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. /**
  18. * @defgroup tnef TNEF reader and writer
  19. * @{
  20. */
  21. /**
  22. * @brief
  23. * This is our TNEF class, which has been specially designed for
  24. * simple TNEF reading and writing.
  25. *
  26. * Currently does not support recipient-table properties.
  27. *
  28. * TNEF has gone through various versions for Microsoft Mail and
  29. * other really old systems, and therefore has an elaborate backwards-
  30. * compatibility system. This means that most properties can be stored
  31. * in both TNEF properties as within a single 'MAPI' property of the
  32. * TNEF stream (0x00069003). We basically discard all the backwards-
  33. * compatibility and write TNEF streams that only work with
  34. * Outlook 2000 or later (maybe also Outlook 97, not sure about that)
  35. * by only writing the TNEF stream properties in 0x00069003.
  36. *
  37. * -- Steve
  38. */
  39. #include <kopano/platform.h>
  40. #include <memory>
  41. #include <mapidefs.h>
  42. #include <mapiutil.h>
  43. #include <mapiguid.h>
  44. #include <kopano/mapiext.h>
  45. #include <kopano/memory.hpp>
  46. #include <kopano/Util.h>
  47. #include <kopano/charset/convert.h>
  48. #include <string>
  49. #include "tnef.h"
  50. using namespace KCHL;
  51. namespace KC {
  52. enum {
  53. ATT_ATTACH_TITLE = 0x18010,
  54. ATT_REQUEST_RES = 0x40009,
  55. ATT_ATTACH_DATA = 0x6800F,
  56. ATT_ATTACH_META_FILE = 0x68011,
  57. ATT_ATTACH_REND_DATA = 0x69002,
  58. ATT_MAPI_PROPS = 0x69003,
  59. ATT_ATTACHMENT = 0x69005,
  60. ATT_MESSAGE_CLASS = 0x78008,
  61. };
  62. // The mapping between Microsoft Mail IPM classes and those used in MAPI
  63. // see: http://msdn2.microsoft.com/en-us/library/ms527360.aspx
  64. static const struct _sClassMap {
  65. const char *szScheduleClass;
  66. const char *szMAPIClass;
  67. } sClassMap[] = {
  68. { "IPM.Microsoft Schedule.MtgReq", "IPM.Schedule.Meeting.Request" },
  69. { "IPM.Microsoft Schedule.MtgRespP", "IPM.Schedule.Meeting.Resp.Pos" },
  70. { "IPM.Microsoft Schedule.MtgRespN", "IPM.Schedule.Meeting.Resp.Neg" },
  71. { "IPM.Microsoft Schedule.MtgRespA", "IPM.Schedule.Meeting.Resp.Tent" },
  72. { "IPM.Microsoft Schedule.MtgCncl", "IPM.Schedule.Meeting.Canceled" },
  73. { "IPM.Microsoft Mail.Non-Delivery", "Report.IPM.Note.NDR" },
  74. { "IPM.Microsoft Mail.Read Receipt", "Report.IPM.Note.IPNRN" },
  75. { "IPM.Microsoft Mail.Note", "IPM.Note" },
  76. { "IPM.Microsoft Mail.Note", "IPM" }
  77. };
  78. static const char *FindMAPIClassByScheduleClass(const char *szSClass)
  79. {
  80. for (size_t i = 0; i < ARRAY_SIZE(sClassMap); ++i)
  81. if(strcasecmp(szSClass, sClassMap[i].szScheduleClass) == 0) {
  82. return sClassMap[i].szMAPIClass;
  83. }
  84. return NULL;
  85. }
  86. /**
  87. * Returns TRUE if the given property tag is in the given property tag array
  88. *
  89. * @param[in] ulPropTag The property tag to find in lpPropList
  90. * @param[in] lpPropList The proptagarray to loop through
  91. * @retval true ulPropTag is alread present in lpPropList
  92. * @retval false ulPropTag is not present in lpPropList
  93. */
  94. static bool PropTagInPropList(ULONG ulPropTag, const SPropTagArray *lpPropList)
  95. {
  96. if (lpPropList == NULL)
  97. return false;
  98. for (ULONG i = 0; i < lpPropList->cValues; ++i)
  99. if (PROP_ID(ulPropTag) == PROP_ID(lpPropList->aulPropTag[i]))
  100. return true;
  101. return false;
  102. }
  103. /**
  104. * ECTNEF constructor, used for base and sub objects in TNEF streams
  105. *
  106. * @param[in] ulFlags TNEF_ENCODE
  107. * @param[in] lpMessage Properties from this message will be saved to lpStream as TNEF data
  108. * @param[in,out] lpStream An existing empty stream to save the propteries to as TNEF data
  109. *
  110. * @param[in] ulFlags TNEF_DECODE
  111. * @param[in,out] lpMessage TNEF properties will be saved to this message, and attachments will be create under this message.
  112. * @param[in] lpStream IStream object to the TNEF data
  113. */
  114. ECTNEF::ECTNEF(ULONG ulFlags, IMessage *lpMessage, IStream *lpStream) :
  115. m_lpStream(lpStream), m_lpMessage(lpMessage)
  116. {
  117. this->ulFlags = ulFlags;
  118. }
  119. /**
  120. * ECTNEF destructor frees allocated memory while handling the TNEF
  121. * stream.
  122. */
  123. ECTNEF::~ECTNEF()
  124. {
  125. for (const auto p : lstProps)
  126. MAPIFreeBuffer(p);
  127. for (const auto a : lstAttachments)
  128. FreeAttachmentData(a);
  129. }
  130. /**
  131. * Frees all allocated memory for attachments found in the TNEF
  132. * stream.
  133. *
  134. * @param[in,out] lpTnefAtt free all data in this attachment and delete the pointer too
  135. */
  136. void ECTNEF::FreeAttachmentData(tnefattachment* lpTnefAtt)
  137. {
  138. delete[] lpTnefAtt->data;
  139. for (const auto p : lpTnefAtt->lstProps)
  140. MAPIFreeBuffer(p);
  141. delete lpTnefAtt;
  142. }
  143. /**
  144. * Read data from lpStream and set in memory as one large
  145. * property. Only used to save MAPI_E_NOT_ENOUGH_MEMORY properties
  146. * from m_lpMessage to a separate LPSPropValue which will be saved in
  147. * the TNEF Stream later in Finish().
  148. *
  149. * @param[in] lpStream Input stream that points to PT_BINARY or PT_UNICODE data
  150. * @param[in] ulPropTag Current data type of lpStream, TYPE part can only contain either PT_BINARY or PT_UNICODE.
  151. * @param[out] lppPropValue Property structure to return data from stream in, with ulPropTag
  152. * @return MAPI error code, stream errors, memory errors.
  153. * @retval MAPI_E_INVALID_PARAMETER invalid lpStream of lppPropValue pointer
  154. * @retval MAPI_E_INVALID_TYPE invalid ulPropTag
  155. */
  156. static HRESULT StreamToPropValue(IStream *lpStream, ULONG ulPropTag,
  157. LPSPropValue *lppPropValue)
  158. {
  159. HRESULT hr = hrSuccess;
  160. memory_ptr<SPropValue> lpPropValue;
  161. STATSTG sStatstg;
  162. ULONG ulRead = 0;
  163. ULONG ulTotal = 0;
  164. BYTE *wptr = NULL;
  165. if (lpStream == NULL || lppPropValue == NULL)
  166. return MAPI_E_INVALID_PARAMETER;
  167. if (PROP_TYPE(ulPropTag) != PT_BINARY && PROP_TYPE(ulPropTag) != PT_UNICODE)
  168. return MAPI_E_INVALID_TYPE;
  169. hr = lpStream->Stat(&sStatstg, 0);
  170. if(hr != hrSuccess)
  171. return hr;
  172. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpPropValue);
  173. if(hr != hrSuccess)
  174. return hr;
  175. lpPropValue->ulPropTag = ulPropTag;
  176. if (PROP_TYPE(ulPropTag) == PT_BINARY) {
  177. lpPropValue->Value.bin.cb = (ULONG)sStatstg.cbSize.QuadPart;
  178. hr = MAPIAllocateMore((ULONG)sStatstg.cbSize.QuadPart, lpPropValue, (void**)&lpPropValue->Value.bin.lpb);
  179. if(hr != hrSuccess)
  180. return hr;
  181. wptr = lpPropValue->Value.bin.lpb;
  182. } else if (PROP_TYPE(ulPropTag) == PT_UNICODE) {
  183. hr = MAPIAllocateMore((ULONG)sStatstg.cbSize.QuadPart + sizeof(WCHAR), lpPropValue, (void**)&lpPropValue->Value.lpszW);
  184. if (hr != hrSuccess)
  185. return hr;
  186. // terminate unicode string
  187. lpPropValue->Value.lpszW[sStatstg.cbSize.QuadPart / sizeof(WCHAR)] = L'\0';
  188. wptr = (BYTE*)lpPropValue->Value.lpszW;
  189. }
  190. while (1) {
  191. hr = lpStream->Read(wptr + ulTotal, 4096, &ulRead);
  192. if (hr != hrSuccess)
  193. return hr;
  194. if (ulRead == 0)
  195. break;
  196. ulTotal += ulRead;
  197. }
  198. *lppPropValue = lpPropValue.release();
  199. return hrSuccess;
  200. }
  201. /**
  202. * Adds the requested properties from the message into the pending
  203. * TNEF stream. String properties in lpPropList must be in
  204. * PT_UNICODE. PT_STRING8 properties will never be added.
  205. *
  206. * @param[in] ulFlags TNEF_PROP_INCLUDE: add only properties from message to stream from the lpPropList, or
  207. * TNEF_PROP_EXCLUDE: add all properties except if listed in lpPropList
  208. * @param[in] lpPropList List of properties to add to the stream if present in m_lpMessage, or
  209. * List of properties to exclude from the message
  210. * @return MAPI error code
  211. */
  212. HRESULT ECTNEF::AddProps(ULONG ulFlags, const SPropTagArray *lpPropList)
  213. {
  214. HRESULT hr = hrSuccess;
  215. memory_ptr<SPropTagArray> lpPropListMessage;
  216. memory_ptr<SPropValue> lpPropValue;
  217. LPSPropValue lpStreamValue = NULL;
  218. SizedSPropTagArray(1, sPropTagArray);
  219. unsigned int i = 0;
  220. bool fPropTagInList = false;
  221. ULONG cValue = 0;
  222. // Loop through all the properties on the message, and only
  223. // add those that we want to add to the list
  224. hr = m_lpMessage->GetPropList(MAPI_UNICODE, &~lpPropListMessage);
  225. if (hr != hrSuccess)
  226. return hr;
  227. for (i = 0; i < lpPropListMessage->cValues; ++i) {
  228. /*
  229. * Do not send properties in 0x67XX range, since these seem to
  230. * be blacklisted in recent exchange servers, which causes
  231. * exchange to drop the entire message.
  232. */
  233. if (PROP_ID(lpPropListMessage->aulPropTag[i]) >= 0x6700 &&
  234. PROP_ID(lpPropListMessage->aulPropTag[i]) <= 0x67FF)
  235. continue;
  236. // unable to save these properties
  237. if(PROP_TYPE(lpPropListMessage->aulPropTag[i]) == PT_OBJECT ||
  238. PROP_TYPE(lpPropListMessage->aulPropTag[i]) == PT_UNSPECIFIED ||
  239. PROP_TYPE(lpPropListMessage->aulPropTag[i]) == PT_NULL)
  240. continue;
  241. fPropTagInList = PropTagInPropList(lpPropListMessage->aulPropTag[i], lpPropList);
  242. bool a = ulFlags & TNEF_PROP_INCLUDE && fPropTagInList;
  243. a |= ulFlags & TNEF_PROP_EXCLUDE && !fPropTagInList;
  244. if (!a)
  245. continue;
  246. sPropTagArray.cValues = 1;
  247. sPropTagArray.aulPropTag[0] = lpPropListMessage->aulPropTag[i];
  248. hr = m_lpMessage->GetProps(sPropTagArray, 0, &cValue, &~lpPropValue);
  249. if (hr == hrSuccess)
  250. lstProps.push_back(lpPropValue.release());
  251. object_ptr<IStream> lpStream;
  252. if (hr == MAPI_W_ERRORS_RETURNED && lpPropValue != NULL &&
  253. lpPropValue->Value.err == MAPI_E_NOT_ENOUGH_MEMORY &&
  254. m_lpMessage->OpenProperty(lpPropListMessage->aulPropTag[i], &IID_IStream, 0, 0, &~lpStream) == hrSuccess) {
  255. hr = StreamToPropValue(lpStream, lpPropListMessage->aulPropTag[i], &lpStreamValue);
  256. if (hr == hrSuccess) {
  257. lstProps.push_back(lpStreamValue);
  258. lpStreamValue = NULL;
  259. }
  260. }
  261. // otherwise silently ignore the property
  262. }
  263. return hrSuccess;
  264. }
  265. /**
  266. * Extracts the properties from the TNEF stream, and sets them in the message
  267. *
  268. * @param[in] ulFlags TNEF_PROP_INCLUDE or TNEF_PROP_EXCLUDE
  269. * @param[in] lpPropList List of properties to include from the stream if present in m_lpMessage or
  270. * List of properties to exclude from the stream if present in m_lpMessage
  271. *
  272. * @retval MAPI_E_CORRUPT_DATA TNEF stream input is broken, or other MAPI error codes
  273. */
  274. HRESULT ECTNEF::ExtractProps(ULONG ulFlags, LPSPropTagArray lpPropList)
  275. {
  276. HRESULT hr = hrSuccess;
  277. ULONG ulSignature = 0;
  278. ULONG ulType = 0;
  279. ULONG ulSize = 0;
  280. unsigned short ulChecksum = 0;
  281. unsigned short ulKey = 0;
  282. unsigned char ulComponent = 0;
  283. memory_ptr<char> lpBuffer;
  284. SPropValue sProp;
  285. std::unique_ptr<char[]> szSClass;
  286. // Attachments props
  287. memory_ptr<SPropValue> lpProp;
  288. tnefattachment* lpTnefAtt = NULL;
  289. hr = HrReadDWord(m_lpStream, &ulSignature);
  290. if(hr != hrSuccess)
  291. goto exit;
  292. // Check signature
  293. if(ulSignature != TNEF_SIGNATURE) {
  294. hr = MAPI_E_CORRUPT_DATA;
  295. goto exit;
  296. }
  297. hr = HrReadWord(m_lpStream, &ulKey);
  298. if(hr != hrSuccess)
  299. goto exit;
  300. // File is made of blocks, with each a type and size. Component and Key are ignored.
  301. while(1) {
  302. hr = HrReadByte(m_lpStream, &ulComponent);
  303. if(hr != hrSuccess) {
  304. hr = hrSuccess; // EOF -> no error
  305. goto exit;
  306. }
  307. hr = HrReadDWord(m_lpStream, &ulType);
  308. if(hr != hrSuccess)
  309. goto exit;
  310. hr = HrReadDWord(m_lpStream, &ulSize);
  311. if(hr != hrSuccess)
  312. goto exit;
  313. if (ulSize == 0) {
  314. // do not allocate 0 size data block
  315. hr = MAPI_E_CORRUPT_DATA;
  316. goto exit;
  317. }
  318. hr = MAPIAllocateBuffer(ulSize, &~lpBuffer);
  319. if(hr != hrSuccess)
  320. goto exit;
  321. hr = HrReadData(m_lpStream, lpBuffer, ulSize);
  322. if(hr != hrSuccess)
  323. goto exit;
  324. hr = HrReadWord(m_lpStream, &ulChecksum);
  325. if(hr != hrSuccess)
  326. goto exit;
  327. // Loop through all the blocks of the TNEF data. We are only interested
  328. // in the properties block for now (0x00069003)
  329. switch(ulType) {
  330. case ATT_MAPI_PROPS:
  331. hr = HrReadPropStream(lpBuffer, ulSize, lstProps);
  332. if (hr != hrSuccess)
  333. goto exit;
  334. break;
  335. case ATT_MESSAGE_CLASS: /* PR_MESSAGE_CLASS */
  336. {
  337. szSClass.reset(new char[ulSize+1]);
  338. char *szMAPIClass = NULL;
  339. // NULL terminate the string
  340. memcpy(szSClass.get(), lpBuffer, ulSize);
  341. szSClass[ulSize] = 0;
  342. // We map the Schedule+ message class to the more modern MAPI message
  343. // class. The mapping should be correct as far as we can find ..
  344. szMAPIClass = (char *)FindMAPIClassByScheduleClass(szSClass.get());
  345. if(szMAPIClass == NULL)
  346. szMAPIClass = szSClass.get(); // mapping not found, use string from TNEF file
  347. sProp.ulPropTag = PR_MESSAGE_CLASS_A;
  348. sProp.Value.lpszA = szMAPIClass;
  349. // We do a 'SetProps' now because we want to override the PR_MESSAGE_CLASS
  350. // setting, while Finish() never overrides already-present properties for
  351. // security reasons.
  352. m_lpMessage->SetProps(1, &sProp, NULL);
  353. break;
  354. }
  355. case 0x00050008: /* PR_OWNER_APPT_ID */
  356. if(ulSize == 4 && lpBuffer) {
  357. sProp.ulPropTag = PR_OWNER_APPT_ID;
  358. sProp.Value.l = *reinterpret_cast<LONG *>(lpBuffer.get());
  359. m_lpMessage->SetProps(1, &sProp, NULL);
  360. }
  361. break;
  362. case ATT_REQUEST_RES: /* PR_RESPONSE_REQUESTED */
  363. if(ulSize == 2 && lpBuffer) {
  364. sProp.ulPropTag = PR_RESPONSE_REQUESTED;
  365. sProp.Value.b = static_cast<bool>(*reinterpret_cast<short *>(lpBuffer.get()));
  366. m_lpMessage->SetProps(1, &sProp, NULL);
  367. }
  368. break;
  369. // --- TNEF attachemnts ---
  370. case ATT_ATTACH_REND_DATA:
  371. // Start marker of attachment
  372. if(ulSize == sizeof(struct AttachRendData) && lpBuffer) {
  373. auto lpData = reinterpret_cast<AttachRendData *>(lpBuffer.get());
  374. if (lpTnefAtt) {
  375. if (lpTnefAtt->data || !lpTnefAtt->lstProps.empty()) // end marker previous attachment
  376. lstAttachments.push_back(lpTnefAtt);
  377. else
  378. FreeAttachmentData(lpTnefAtt);
  379. }
  380. lpTnefAtt = new tnefattachment;
  381. lpTnefAtt->size = 0;
  382. lpTnefAtt->data = NULL;
  383. lpTnefAtt->rdata = *lpData;
  384. }
  385. break;
  386. case ATT_ATTACH_TITLE: // PR_ATTACH_FILENAME
  387. if (!lpTnefAtt) {
  388. hr = MAPI_E_CORRUPT_DATA;
  389. goto exit;
  390. }
  391. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpProp);
  392. if (hr != hrSuccess)
  393. goto exit;
  394. lpProp->ulPropTag = PR_ATTACH_FILENAME_A;
  395. if ((hr = MAPIAllocateMore(ulSize, lpProp, (void**)&lpProp->Value.lpszA)) != hrSuccess)
  396. goto exit;
  397. memcpy(lpProp->Value.lpszA, lpBuffer, ulSize);
  398. lpTnefAtt->lstProps.push_back(lpProp.release());
  399. break;
  400. case ATT_ATTACH_META_FILE:
  401. // PR_ATTACH_RENDERING, extra icon information
  402. if (!lpTnefAtt) {
  403. hr = MAPI_E_CORRUPT_DATA;
  404. goto exit;
  405. }
  406. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpProp);
  407. if (hr != hrSuccess)
  408. goto exit;
  409. lpProp->ulPropTag = PR_ATTACH_RENDERING;
  410. if ((hr = MAPIAllocateMore(ulSize, lpProp, (void**)&lpProp->Value.bin.lpb)) != hrSuccess)
  411. goto exit;
  412. lpProp->Value.bin.cb = ulSize;
  413. memcpy(lpProp->Value.bin.lpb, lpBuffer, ulSize);
  414. lpTnefAtt->lstProps.push_back(lpProp.release());
  415. break;
  416. case ATT_ATTACH_DATA:
  417. // PR_ATTACH_DATA_BIN, will be set via OpenProperty() in ECTNEF::Finish()
  418. if (!lpTnefAtt) {
  419. hr = MAPI_E_CORRUPT_DATA;
  420. goto exit;
  421. }
  422. lpTnefAtt->size = ulSize;
  423. lpTnefAtt->data = new BYTE[ulSize];
  424. memcpy(lpTnefAtt->data, lpBuffer, ulSize);
  425. break;
  426. case ATT_ATTACHMENT: // Attachment property stream
  427. if (!lpTnefAtt) {
  428. hr = MAPI_E_CORRUPT_DATA;
  429. goto exit;
  430. }
  431. hr = HrReadPropStream(lpBuffer, ulSize, lpTnefAtt->lstProps);
  432. if (hr != hrSuccess)
  433. goto exit;
  434. break;
  435. default:
  436. // Ignore this block
  437. break;
  438. }
  439. }
  440. exit:
  441. if (lpTnefAtt) {
  442. if (lpTnefAtt->data || !lpTnefAtt->lstProps.empty()) // attachment should be complete before adding
  443. lstAttachments.push_back(lpTnefAtt);
  444. else
  445. FreeAttachmentData(lpTnefAtt);
  446. }
  447. return hr;
  448. }
  449. /**
  450. * Write the properties from a list to the TNEF stream.
  451. *
  452. * @param[in,out] lpStream The TNEF stream to write to
  453. * @param[in] proplist std::list of properties to write in the stream.
  454. * @return MAPI error code
  455. */
  456. HRESULT ECTNEF::HrWritePropStream(IStream *lpStream, std::list<SPropValue *> &proplist)
  457. {
  458. HRESULT hr = HrWriteDWord(lpStream, proplist.size());
  459. if(hr != hrSuccess)
  460. return hr;
  461. for (const auto p : proplist) {
  462. hr = HrWriteSingleProp(lpStream, p);
  463. if (hr != hrSuccess)
  464. return hr;
  465. }
  466. return hr;
  467. }
  468. /**
  469. * Write one property to the TNEF stream.
  470. *
  471. * @param[in,out] lpStream The TNEF stream to write to
  472. * @param[in] lpProp MAPI property to write to the TNEF stream
  473. * @return MAPI error code
  474. */
  475. HRESULT ECTNEF::HrWriteSingleProp(IStream *lpStream, LPSPropValue lpProp)
  476. {
  477. HRESULT hr = hrSuccess;
  478. SizedSPropTagArray(1, sPropTagArray);
  479. ULONG cNames = 0;
  480. memory_ptr<MAPINAMEID *> lppNames;
  481. ULONG ulLen = 0;
  482. ULONG ulMVProp = 0;
  483. ULONG ulCount = 0;
  484. convert_context converter;
  485. std::u16string ucs2;
  486. if(PROP_ID(lpProp->ulPropTag) >= 0x8000) {
  487. memory_ptr<SPropTagArray> lpsPropTagArray;
  488. // Get named property GUID and ID or name
  489. sPropTagArray.cValues = 1;
  490. sPropTagArray.aulPropTag[0] = lpProp->ulPropTag;
  491. hr = Util::HrCopyPropTagArray(sPropTagArray, &~lpsPropTagArray);
  492. if (hr != hrSuccess)
  493. return hr;
  494. hr = m_lpMessage->GetNamesFromIDs(&+lpsPropTagArray, NULL, 0, &cNames, &~lppNames);
  495. if(hr != hrSuccess)
  496. return hrSuccess;
  497. if (cNames == 0 || lppNames == nullptr || lppNames[0] == nullptr)
  498. return MAPI_E_INVALID_PARAMETER;
  499. // Write the property tag
  500. hr = HrWriteDWord(lpStream, lpProp->ulPropTag);
  501. if(hr != hrSuccess)
  502. return hr;
  503. hr = HrWriteData(lpStream, (char *)lppNames[0]->lpguid, sizeof(GUID));
  504. if(hr != hrSuccess)
  505. return hr;
  506. if(lppNames[0]->ulKind == MNID_ID) {
  507. hr = HrWriteDWord(lpStream, 0);
  508. if(hr != hrSuccess)
  509. return hr;
  510. hr = HrWriteDWord(lpStream, lppNames[0]->Kind.lID);
  511. if(hr != hrSuccess)
  512. return hr;
  513. } else {
  514. hr = HrWriteDWord(lpStream, 1);
  515. if(hr != hrSuccess)
  516. return hr;
  517. ucs2 = converter.convert_to<std::u16string>(lppNames[0]->Kind.lpwstrName);
  518. ulLen = ucs2.length() * sizeof(std::u16string::value_type) + sizeof(std::u16string::value_type);
  519. hr = HrWriteDWord(lpStream, ulLen);
  520. if(hr != hrSuccess)
  521. return hr;
  522. hr = HrWriteData(lpStream, (char *)ucs2.c_str(), ulLen);
  523. if(hr != hrSuccess)
  524. return hr;
  525. // Align to 4-byte boundary
  526. while(ulLen & 3) {
  527. hr = HrWriteByte(lpStream, 0);
  528. if(hr != hrSuccess)
  529. return hr;
  530. ++ulLen;
  531. }
  532. }
  533. } else {
  534. // Write the property tag
  535. hr = HrWriteDWord(lpStream, lpProp->ulPropTag);
  536. if(hr != hrSuccess)
  537. return hr;
  538. }
  539. // Now, write the actual property value
  540. if(PROP_TYPE(lpProp->ulPropTag) & MV_FLAG) {
  541. switch(PROP_TYPE(lpProp->ulPropTag)) {
  542. case PT_MV_I2:
  543. ulCount = lpProp->Value.MVi.cValues;
  544. break;
  545. case PT_MV_LONG:
  546. ulCount = lpProp->Value.MVl.cValues;
  547. break;
  548. case PT_MV_R4:
  549. ulCount = lpProp->Value.MVflt.cValues;
  550. break;
  551. case PT_MV_APPTIME:
  552. ulCount = lpProp->Value.MVat.cValues;
  553. break;
  554. case PT_MV_DOUBLE:
  555. ulCount = lpProp->Value.MVdbl.cValues;
  556. break;
  557. case PT_MV_CURRENCY:
  558. ulCount = lpProp->Value.MVcur.cValues;
  559. break;
  560. case PT_MV_SYSTIME:
  561. ulCount = lpProp->Value.MVft.cValues;
  562. break;
  563. case PT_MV_I8:
  564. ulCount = lpProp->Value.MVli.cValues;
  565. break;
  566. case PT_MV_STRING8:
  567. ulCount = lpProp->Value.MVszA.cValues;
  568. break;
  569. case PT_MV_UNICODE:
  570. ulCount = lpProp->Value.MVszW.cValues;
  571. break;
  572. case PT_MV_BINARY:
  573. ulCount = lpProp->Value.MVbin.cValues;
  574. break;
  575. case PT_MV_CLSID:
  576. ulCount = lpProp->Value.MVguid.cValues;
  577. break;
  578. default:
  579. return MAPI_E_INVALID_PARAMETER;
  580. }
  581. hr = HrWriteDWord(lpStream, ulCount);
  582. } else {
  583. ulCount = 1;
  584. }
  585. ulMVProp = 0;
  586. for (ulMVProp = 0; ulMVProp < ulCount; ++ulMVProp) {
  587. switch(PROP_TYPE(lpProp->ulPropTag) &~ MV_FLAG) {
  588. case PT_I2:
  589. if(lpProp->ulPropTag & MV_FLAG)
  590. hr = HrWriteDWord(lpStream,lpProp->Value.MVi.lpi[ulMVProp]);
  591. else
  592. hr = HrWriteDWord(lpStream,lpProp->Value.i);
  593. break;
  594. case PT_LONG:
  595. if(lpProp->ulPropTag & MV_FLAG)
  596. hr = HrWriteDWord(lpStream,lpProp->Value.MVl.lpl[ulMVProp]);
  597. else
  598. hr = HrWriteDWord(lpStream,lpProp->Value.ul);
  599. break;
  600. case PT_BOOLEAN:
  601. hr = HrWriteDWord(lpStream, lpProp->Value.b);
  602. break;
  603. case PT_R4:
  604. if(lpProp->ulPropTag & MV_FLAG)
  605. hr = HrWriteData(lpStream,(char *)&lpProp->Value.MVflt.lpflt[ulMVProp], sizeof(float));
  606. else
  607. hr = HrWriteData(lpStream,(char *)&lpProp->Value.flt, sizeof(float));
  608. break;
  609. case PT_APPTIME:
  610. if(lpProp->ulPropTag & MV_FLAG)
  611. hr = HrWriteData(lpStream,(char *)&lpProp->Value.MVat.lpat[ulMVProp], sizeof(double));
  612. else
  613. hr = HrWriteData(lpStream,(char *)&lpProp->Value.at, sizeof(double));
  614. break;
  615. case PT_DOUBLE:
  616. if(lpProp->ulPropTag & MV_FLAG)
  617. hr = HrWriteData(lpStream,(char *)&lpProp->Value.MVdbl.lpdbl[ulMVProp], sizeof(double));
  618. else
  619. hr = HrWriteData(lpStream,(char *)&lpProp->Value.dbl, sizeof(double));
  620. break;
  621. case PT_CURRENCY:
  622. if(lpProp->ulPropTag & MV_FLAG) {
  623. hr = HrWriteDWord(lpStream, lpProp->Value.MVcur.lpcur[ulMVProp].Lo);
  624. if(hr != hrSuccess)
  625. return hr;
  626. hr = HrWriteDWord(lpStream, lpProp->Value.MVcur.lpcur[ulMVProp].Hi);
  627. } else {
  628. hr = HrWriteDWord(lpStream, lpProp->Value.cur.Lo);
  629. if(hr != hrSuccess)
  630. return hr;
  631. hr = HrWriteDWord(lpStream, lpProp->Value.cur.Hi);
  632. }
  633. if (hr != hrSuccess)
  634. return hr;
  635. break;
  636. case PT_SYSTIME:
  637. if(lpProp->ulPropTag & MV_FLAG) {
  638. hr = HrWriteDWord(lpStream, lpProp->Value.MVft.lpft[ulMVProp].dwLowDateTime);
  639. if(hr != hrSuccess)
  640. return hr;
  641. hr = HrWriteDWord(lpStream, lpProp->Value.MVft.lpft[ulMVProp].dwHighDateTime);
  642. } else {
  643. hr = HrWriteDWord(lpStream, lpProp->Value.ft.dwLowDateTime);
  644. if(hr != hrSuccess)
  645. return hr;
  646. hr = HrWriteDWord(lpStream, lpProp->Value.ft.dwHighDateTime);
  647. }
  648. if (hr != hrSuccess)
  649. return hr;
  650. break;
  651. case PT_I8:
  652. if(lpProp->ulPropTag & MV_FLAG) {
  653. hr = HrWriteDWord(lpStream, lpProp->Value.MVli.lpli[ulMVProp].LowPart);
  654. if(hr != hrSuccess)
  655. return hr;
  656. hr = HrWriteDWord(lpStream, lpProp->Value.MVli.lpli[ulMVProp].HighPart);
  657. } else {
  658. hr = HrWriteDWord(lpStream, lpProp->Value.li.LowPart);
  659. if(hr != hrSuccess)
  660. return hr;
  661. hr = HrWriteDWord(lpStream, lpProp->Value.li.HighPart);
  662. }
  663. if (hr != hrSuccess)
  664. return hr;
  665. break;
  666. case PT_STRING8:
  667. if(lpProp->ulPropTag & MV_FLAG) {
  668. ulLen = strlen(lpProp->Value.MVszA.lppszA[ulMVProp])+1;
  669. hr = HrWriteDWord(lpStream, ulLen);
  670. if(hr != hrSuccess)
  671. return hr;
  672. hr = HrWriteData(lpStream, lpProp->Value.MVszA.lppszA[ulMVProp], ulLen);
  673. } else {
  674. ulLen = strlen(lpProp->Value.lpszA)+1;
  675. hr = HrWriteDWord(lpStream, 1); // unknown why this is here
  676. if(hr != hrSuccess)
  677. return hr;
  678. hr = HrWriteDWord(lpStream, ulLen);
  679. if(hr != hrSuccess)
  680. return hr;
  681. hr = HrWriteData(lpStream, lpProp->Value.lpszA, ulLen);
  682. }
  683. if (hr != hrSuccess)
  684. return hr;
  685. // Align to 4-byte boundary
  686. while(ulLen & 3) {
  687. hr = HrWriteByte(lpStream, 0);
  688. if (hr != hrSuccess)
  689. return hr;
  690. ++ulLen;
  691. }
  692. break;
  693. case PT_UNICODE:
  694. // Make sure we write UCS-2, since that's the format of PT_UNICODE in Win32.
  695. if(lpProp->ulPropTag & MV_FLAG) {
  696. ucs2 = converter.convert_to<std::u16string>(lpProp->Value.MVszW.lppszW[ulMVProp]);
  697. ulLen = ucs2.length() * sizeof(std::u16string::value_type) + sizeof(std::u16string::value_type);
  698. hr = HrWriteDWord(lpStream, ulLen);
  699. if(hr != hrSuccess)
  700. return hr;
  701. hr = HrWriteData(lpStream, (char *)ucs2.c_str(), ulLen);
  702. } else {
  703. ucs2 = converter.convert_to<std::u16string>(lpProp->Value.lpszW);
  704. ulLen = ucs2.length() * sizeof(std::u16string::value_type) + sizeof(std::u16string::value_type);
  705. hr = HrWriteDWord(lpStream, 1); // unknown why this is here
  706. if(hr != hrSuccess)
  707. return hr;
  708. hr = HrWriteDWord(lpStream, ulLen);
  709. if(hr != hrSuccess)
  710. return hr;
  711. hr = HrWriteData(lpStream, (char *)ucs2.c_str(), ulLen);
  712. }
  713. if (hr != hrSuccess)
  714. return hr;
  715. // Align to 4-byte boundary
  716. while(ulLen & 3) {
  717. hr = HrWriteByte(lpStream, 0);
  718. if (hr != hrSuccess)
  719. return hr;
  720. ++ulLen;
  721. }
  722. break;
  723. case PT_OBJECT:
  724. case PT_BINARY:
  725. if(lpProp->ulPropTag & MV_FLAG) {
  726. ulLen = lpProp->Value.MVbin.lpbin[ulMVProp].cb;
  727. hr = HrWriteDWord(lpStream, ulLen);
  728. if(hr != hrSuccess)
  729. return hr;
  730. hr = HrWriteData(lpStream, (char *)lpProp->Value.MVbin.lpbin[ulMVProp].lpb, ulLen);
  731. } else {
  732. ulLen = lpProp->Value.bin.cb;
  733. hr = HrWriteDWord(lpStream, 1); // unknown why this is here
  734. if(hr != hrSuccess)
  735. return hr;
  736. hr = HrWriteDWord(lpStream, ulLen + (PROP_TYPE(lpProp->ulPropTag) == PT_OBJECT ? sizeof(GUID) : 0));
  737. if(hr != hrSuccess)
  738. return hr;
  739. if(PROP_TYPE(lpProp->ulPropTag) == PT_OBJECT)
  740. HrWriteData(lpStream, (char *)&IID_IStorage, sizeof(GUID));
  741. hr = HrWriteData(lpStream, (char *)lpProp->Value.bin.lpb, ulLen);
  742. }
  743. if (hr != hrSuccess)
  744. return hr;
  745. // Align to 4-byte boundary
  746. while(ulLen & 3) {
  747. hr = HrWriteByte(lpStream, 0);
  748. if (hr != hrSuccess)
  749. return hr;
  750. ++ulLen;
  751. }
  752. break;
  753. case PT_CLSID:
  754. if (lpProp->ulPropTag & MV_FLAG)
  755. hr = HrWriteData(lpStream, (char *)&lpProp->Value.MVguid.lpguid[ulMVProp], sizeof(GUID));
  756. else
  757. hr = HrWriteData(lpStream, (char *)lpProp->Value.lpguid, sizeof(GUID));
  758. if (hr != hrSuccess)
  759. return hr;
  760. break;
  761. default:
  762. hr = MAPI_E_INVALID_PARAMETER;
  763. }
  764. }
  765. return hr;
  766. }
  767. /**
  768. * Read from lpBuffer with size ulSize TNEF properties, and save those
  769. * in the proplist.
  770. *
  771. * @param[in] lpBuffer (part of) a TNEF stream which contains properties
  772. * @param[in] ulSize size of contents in lpBuffer
  773. * @param[in,out] proplist reference to an existing porplist to append properties to
  774. * @return MAPI error code
  775. */
  776. HRESULT ECTNEF::HrReadPropStream(const char *lpBuffer, ULONG ulSize,
  777. std::list<SPropValue *> &proplist)
  778. {
  779. ULONG ulRead = 0;
  780. ULONG ulProps = 0;
  781. LPSPropValue lpProp = NULL;
  782. HRESULT hr = hrSuccess;
  783. ulProps = *reinterpret_cast<const ULONG *>(lpBuffer);
  784. lpBuffer += 4;
  785. ulSize -= 4;
  786. // Loop through all the properties in the data and add them to our internal list
  787. while(ulProps) {
  788. hr = HrReadSingleProp(lpBuffer, ulSize, &ulRead, &lpProp);
  789. if(hr != hrSuccess)
  790. break;
  791. ulSize -= ulRead;
  792. lpBuffer += ulRead;
  793. proplist.push_back(lpProp);
  794. --ulProps;
  795. if(ulRead & 3) {
  796. // Skip padding
  797. lpBuffer += 4 - (ulRead & 3);
  798. }
  799. }
  800. return hr;
  801. }
  802. /**
  803. * Read one property from a TNEF block in a buffer, and return the
  804. * read property and bytes read from the stream.
  805. *
  806. * @param[in] lpBuffer TNEF stream buffer
  807. * @param[in] ulSize size of lpBuffer
  808. * @param[out] lpulRead number of bytes read from lpBuffer to make lppProp
  809. * @param[out] lppProp returns MAPIAllocateBuffer allocated pointer if return is hrSuccess
  810. * @return MAPI error code
  811. */
  812. HRESULT ECTNEF::HrReadSingleProp(const char *lpBuffer, ULONG ulSize,
  813. ULONG *lpulRead, LPSPropValue *lppProp)
  814. {
  815. HRESULT hr = hrSuccess;
  816. ULONG ulPropTag = 0;
  817. ULONG ulLen = 0;
  818. ULONG ulOrigSize = ulSize;
  819. ULONG ulIsNameId = 0;
  820. ULONG ulCount = 0;
  821. ULONG ulMVProp = 0;
  822. memory_ptr<SPropValue> lpProp;
  823. GUID sGuid;
  824. MAPINAMEID sNameID;
  825. LPMAPINAMEID lpNameID = &sNameID;
  826. memory_ptr<SPropTagArray> lpPropTags;
  827. std::wstring strUnicodeName;
  828. std::u16string ucs2;
  829. if(ulSize < 8)
  830. return MAPI_E_NOT_FOUND;
  831. ulPropTag = *reinterpret_cast<const ULONG *>(lpBuffer);
  832. lpBuffer += sizeof(ULONG);
  833. ulSize -= 4;
  834. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpProp);
  835. if(hr != hrSuccess)
  836. return hr;
  837. if(PROP_ID(ulPropTag) >= 0x8000) {
  838. // Named property, first read GUID, then name/id
  839. if (ulSize < 24)
  840. return MAPI_E_CORRUPT_DATA;
  841. memcpy(&sGuid, lpBuffer, sizeof(GUID));
  842. lpBuffer += sizeof(GUID);
  843. ulSize -= sizeof(GUID);
  844. ulIsNameId = *reinterpret_cast<const ULONG *>(lpBuffer);
  845. lpBuffer += 4;
  846. ulSize -= 4;
  847. if(ulIsNameId != 0) {
  848. // A string name follows
  849. ulLen = *reinterpret_cast<const ULONG *>(lpBuffer);
  850. lpBuffer += 4;
  851. ulSize -= 4;
  852. if (ulLen > ulSize)
  853. return MAPI_E_CORRUPT_DATA;
  854. // copy through u16string so we can set the boundary to the given length
  855. ucs2.assign(reinterpret_cast<const std::u16string::value_type *>(lpBuffer), ulLen / sizeof(std::u16string::value_type));
  856. strUnicodeName = convert_to<std::wstring>(ucs2);
  857. sNameID.ulKind = MNID_STRING;
  858. sNameID.Kind.lpwstrName = (WCHAR *)strUnicodeName.c_str();
  859. lpBuffer += ulLen;
  860. ulSize -= ulLen;
  861. // Re-align
  862. lpBuffer += ulLen & 3 ? 4 - (ulLen & 3) : 0;
  863. ulSize -= ulLen & 3 ? 4 - (ulLen & 3) : 0;
  864. } else {
  865. sNameID.ulKind = MNID_ID;
  866. sNameID.Kind.lID = *reinterpret_cast<const ULONG *>(lpBuffer);
  867. lpBuffer += 4;
  868. ulSize -= 4;
  869. }
  870. sNameID.lpguid = &sGuid;
  871. hr = m_lpMessage->GetIDsFromNames(1, &lpNameID, MAPI_CREATE, &~lpPropTags);
  872. if(hr != hrSuccess)
  873. return hr;
  874. // Use the mapped ID, not the original ID. The original ID is discarded
  875. ulPropTag = PROP_TAG(PROP_TYPE(ulPropTag), PROP_ID(lpPropTags->aulPropTag[0]));
  876. }
  877. if(ulPropTag & MV_FLAG) {
  878. if (ulSize < 4)
  879. return MAPI_E_CORRUPT_DATA;
  880. ulCount = *(ULONG *)lpBuffer;
  881. lpBuffer += 4;
  882. ulSize -= 4;
  883. switch(PROP_TYPE(ulPropTag)) {
  884. case PT_MV_I2:
  885. lpProp->Value.MVi.cValues = ulCount;
  886. hr = MAPIAllocateMore(ulCount * sizeof(unsigned short), lpProp, (void **)&lpProp->Value.MVi.lpi);
  887. break;
  888. case PT_MV_LONG:
  889. lpProp->Value.MVl.cValues = ulCount;
  890. hr = MAPIAllocateMore(ulCount * sizeof(ULONG), lpProp, (void **)&lpProp->Value.MVl.lpl);
  891. break;
  892. case PT_MV_R4:
  893. lpProp->Value.MVflt.cValues = ulCount;
  894. hr = MAPIAllocateMore(ulCount * sizeof(float), lpProp, (void **)&lpProp->Value.MVflt.lpflt);
  895. break;
  896. case PT_MV_APPTIME:
  897. lpProp->Value.MVat.cValues = ulCount;
  898. hr = MAPIAllocateMore(ulCount * sizeof(double), lpProp, (void **)&lpProp->Value.MVat.lpat);
  899. break;
  900. case PT_MV_DOUBLE:
  901. lpProp->Value.MVdbl.cValues = ulCount;
  902. hr = MAPIAllocateMore(ulCount * sizeof(double), lpProp, (void **)&lpProp->Value.MVdbl.lpdbl);
  903. break;
  904. case PT_MV_CURRENCY:
  905. lpProp->Value.MVcur.cValues = ulCount;
  906. hr = MAPIAllocateMore(ulCount * sizeof(CURRENCY), lpProp, (void **)&lpProp->Value.MVcur.lpcur);
  907. break;
  908. case PT_MV_SYSTIME:
  909. lpProp->Value.MVft.cValues = ulCount;
  910. hr = MAPIAllocateMore(ulCount * sizeof(FILETIME), lpProp, (void **)&lpProp->Value.MVft.lpft);
  911. break;
  912. case PT_MV_I8:
  913. lpProp->Value.MVli.cValues = ulCount;
  914. hr = MAPIAllocateMore(ulCount * sizeof(LARGE_INTEGER), lpProp, (void **)&lpProp->Value.MVli.lpli);
  915. break;
  916. case PT_MV_STRING8:
  917. lpProp->Value.MVszA.cValues = ulCount;
  918. hr = MAPIAllocateMore(ulCount * sizeof(char *), lpProp, (void **)&lpProp->Value.MVszA.lppszA);
  919. break;
  920. case PT_MV_UNICODE:
  921. lpProp->Value.MVszW.cValues = ulCount;
  922. hr = MAPIAllocateMore(ulCount * sizeof(WCHAR *), lpProp, (void **)&lpProp->Value.MVszW.lppszW);
  923. break;
  924. case PT_MV_BINARY:
  925. lpProp->Value.MVbin.cValues = ulCount;
  926. hr = MAPIAllocateMore(ulCount * sizeof(SBinary), lpProp, (void **)&lpProp->Value.MVbin.lpbin);
  927. break;
  928. case PT_MV_CLSID:
  929. lpProp->Value.MVguid.cValues = ulCount;
  930. hr = MAPIAllocateMore(ulCount * sizeof(GUID), lpProp, (void **)&lpProp->Value.MVguid.lpguid);
  931. break;
  932. default:
  933. return MAPI_E_INVALID_PARAMETER;
  934. }
  935. } else {
  936. ulCount = 1;
  937. }
  938. if(hr != hrSuccess)
  939. return hr;
  940. lpProp->ulPropTag = ulPropTag;
  941. for (ulMVProp = 0; ulMVProp < ulCount; ++ulMVProp) {
  942. switch(PROP_TYPE(ulPropTag) & ~MV_FLAG) {
  943. case PT_I2:
  944. if(ulPropTag & MV_FLAG)
  945. lpProp->Value.MVi.lpi[ulMVProp] = *reinterpret_cast<const unsigned short *>(lpBuffer);
  946. else
  947. lpProp->Value.i = *reinterpret_cast<const unsigned short *>(lpBuffer);
  948. lpBuffer += 4;
  949. ulSize -= 4;
  950. break;
  951. case PT_LONG:
  952. if(ulPropTag & MV_FLAG)
  953. lpProp->Value.MVl.lpl[ulMVProp] = *reinterpret_cast<const ULONG *>(lpBuffer);
  954. else
  955. lpProp->Value.ul = *reinterpret_cast<const ULONG *>(lpBuffer);
  956. lpBuffer += 4;
  957. ulSize -= 4;
  958. break;
  959. case PT_BOOLEAN:
  960. lpProp->Value.b = *reinterpret_cast<const BOOL *>(lpBuffer);
  961. lpBuffer += 4;
  962. ulSize -= 4;
  963. break;
  964. case PT_R4:
  965. if(ulPropTag & MV_FLAG)
  966. lpProp->Value.MVflt.lpflt[ulMVProp] = *reinterpret_cast<const float *>(lpBuffer);
  967. else
  968. lpProp->Value.flt = *reinterpret_cast<const float *>(lpBuffer);
  969. lpBuffer += 4;
  970. ulSize -= 4;
  971. break;
  972. case PT_APPTIME:
  973. if (ulSize < 8)
  974. return MAPI_E_CORRUPT_DATA;
  975. if(ulPropTag & MV_FLAG)
  976. lpProp->Value.MVat.lpat[ulMVProp] = *reinterpret_cast<const double *>(lpBuffer);
  977. else
  978. lpProp->Value.at = *reinterpret_cast<const double *>(lpBuffer);
  979. lpBuffer += 8;
  980. ulSize -= 8;
  981. break;
  982. case PT_DOUBLE:
  983. if (ulSize < 8)
  984. return MAPI_E_CORRUPT_DATA;
  985. if(ulPropTag & MV_FLAG)
  986. lpProp->Value.MVdbl.lpdbl[ulMVProp] = *reinterpret_cast<const double *>(lpBuffer);
  987. else
  988. lpProp->Value.dbl = *reinterpret_cast<const double *>(lpBuffer);
  989. lpBuffer += 8;
  990. ulSize -= 8;
  991. break;
  992. case PT_CURRENCY:
  993. if (ulSize < 8)
  994. return MAPI_E_CORRUPT_DATA;
  995. if(ulPropTag & MV_FLAG) {
  996. lpProp->Value.MVcur.lpcur[ulMVProp].Lo = *reinterpret_cast<const ULONG *>(lpBuffer);
  997. lpProp->Value.MVcur.lpcur[ulMVProp].Hi = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
  998. } else {
  999. lpProp->Value.cur.Lo = *reinterpret_cast<const ULONG *>(lpBuffer);
  1000. lpProp->Value.cur.Hi = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
  1001. }
  1002. lpBuffer += 8;
  1003. ulSize -= 8;
  1004. break;
  1005. case PT_SYSTIME:
  1006. if (ulSize < 8)
  1007. return MAPI_E_CORRUPT_DATA;
  1008. if(ulPropTag & MV_FLAG) {
  1009. lpProp->Value.MVft.lpft[ulMVProp].dwLowDateTime = *reinterpret_cast<const ULONG *>(lpBuffer);
  1010. lpProp->Value.MVft.lpft[ulMVProp].dwHighDateTime = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
  1011. } else {
  1012. lpProp->Value.ft.dwLowDateTime = *reinterpret_cast<const ULONG *>(lpBuffer);
  1013. lpProp->Value.ft.dwHighDateTime = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
  1014. }
  1015. lpBuffer += 8;
  1016. ulSize -= 8;
  1017. break;
  1018. case PT_I8:
  1019. if (ulSize < 8)
  1020. return MAPI_E_CORRUPT_DATA;
  1021. if(ulPropTag & MV_FLAG) {
  1022. lpProp->Value.MVli.lpli[ulMVProp].LowPart = *reinterpret_cast<const ULONG *>(lpBuffer);
  1023. lpProp->Value.MVli.lpli[ulMVProp].HighPart = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
  1024. } else {
  1025. lpProp->Value.li.LowPart = *reinterpret_cast<const ULONG *>(lpBuffer);
  1026. lpProp->Value.li.HighPart = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
  1027. }
  1028. lpBuffer += 8;
  1029. ulSize -= 8;
  1030. break;
  1031. case PT_STRING8:
  1032. if (ulSize < 8)
  1033. return MAPI_E_CORRUPT_DATA;
  1034. if((PROP_TYPE(ulPropTag) & MV_FLAG) == 0) {
  1035. lpBuffer += 4; // Skip next 4 bytes, they are always '1'
  1036. ulSize -= 4;
  1037. }
  1038. ulLen = *reinterpret_cast<const ULONG *>(lpBuffer);
  1039. lpBuffer += 4;
  1040. ulSize -= 4;
  1041. if (ulSize < ulLen)
  1042. return MAPI_E_CORRUPT_DATA;
  1043. if(ulPropTag & MV_FLAG) {
  1044. hr = MAPIAllocateMore(ulLen+1, lpProp, (void **)&lpProp->Value.MVszA.lppszA[ulMVProp]);
  1045. if(hr != hrSuccess)
  1046. return hr;
  1047. memcpy(lpProp->Value.MVszA.lppszA[ulMVProp], lpBuffer, ulLen);
  1048. lpProp->Value.MVszA.lppszA[ulMVProp][ulLen] = 0; // should be terminated anyway but we terminte it just to be sure
  1049. } else {
  1050. hr = MAPIAllocateMore(ulLen+1, lpProp, (void **)&lpProp->Value.lpszA);
  1051. if(hr != hrSuccess)
  1052. return hr;
  1053. memcpy(lpProp->Value.lpszA, lpBuffer, ulLen);
  1054. lpProp->Value.lpszA[ulLen] = 0; // should be terminated anyway but we terminte it just to be sure
  1055. }
  1056. lpBuffer += ulLen;
  1057. ulSize -= ulLen;
  1058. // Re-align
  1059. lpBuffer += ulLen & 3 ? 4 - (ulLen & 3) : 0;
  1060. ulSize -= ulLen & 3 ? 4 - (ulLen & 3) : 0;
  1061. break;
  1062. case PT_UNICODE:
  1063. // Make sure we read UCS-2, since that is the format of PT_UNICODE in Win32.
  1064. if (ulSize < 8)
  1065. return MAPI_E_CORRUPT_DATA;
  1066. if((PROP_TYPE(ulPropTag) & MV_FLAG) == 0) {
  1067. lpBuffer += 4; // Skip next 4 bytes, they are always '1'
  1068. ulSize -= 4;
  1069. }
  1070. ulLen = *reinterpret_cast<const ULONG *>(lpBuffer); // Assumes 'len' in file is BYTES, not chars
  1071. lpBuffer += 4;
  1072. ulSize -= 4;
  1073. if (ulSize < ulLen)
  1074. return MAPI_E_CORRUPT_DATA;
  1075. // copy through u16string so we can set the boundary to the given length
  1076. ucs2.assign(reinterpret_cast<const std::u16string::value_type *>(lpBuffer), ulLen / sizeof(std::u16string::value_type));
  1077. strUnicodeName = convert_to<std::wstring>(ucs2);
  1078. if(ulPropTag & MV_FLAG) {
  1079. hr = MAPIAllocateMore((strUnicodeName.length()+1) * sizeof(WCHAR), lpProp, (void **)&lpProp->Value.MVszW.lppszW[ulMVProp]);
  1080. if(hr != hrSuccess)
  1081. return hr;
  1082. wcscpy(lpProp->Value.MVszW.lppszW[ulMVProp], strUnicodeName.c_str());
  1083. } else {
  1084. hr = MAPIAllocateMore((strUnicodeName.length()+1) * sizeof(WCHAR), lpProp, (void **)&lpProp->Value.lpszW);
  1085. if(hr != hrSuccess)
  1086. return hr;
  1087. wcscpy(lpProp->Value.lpszW, strUnicodeName.c_str());
  1088. }
  1089. lpBuffer += ulLen;
  1090. ulSize -= ulLen;
  1091. // Re-align
  1092. lpBuffer += ulLen & 3 ? 4 - (ulLen & 3) : 0;
  1093. ulSize -= ulLen & 3 ? 4 - (ulLen & 3) : 0;
  1094. break;
  1095. case PT_OBJECT: // PST sends PT_OBJECT data. Treat as PT_BINARY
  1096. case PT_BINARY:
  1097. if (ulSize < 8)
  1098. return MAPI_E_CORRUPT_DATA;
  1099. if((PROP_TYPE(ulPropTag) & MV_FLAG) == 0) {
  1100. lpBuffer += 4; // Skip next 4 bytes, it's always '1' (ULONG)
  1101. ulSize -= 4;
  1102. }
  1103. ulLen = *reinterpret_cast<const ULONG *>(lpBuffer);
  1104. lpBuffer += 4;
  1105. ulSize -= 4;
  1106. if (PROP_TYPE(ulPropTag) == PT_OBJECT) {
  1107. // Can be IID_IMessage, IID_IStorage, IID_IStream (and possibly others)
  1108. lpBuffer += 16;
  1109. ulSize -= 16;
  1110. ulLen -= 16;
  1111. }
  1112. if (ulSize < ulLen)
  1113. return MAPI_E_CORRUPT_DATA;
  1114. if(ulPropTag & MV_FLAG) {
  1115. hr = MAPIAllocateMore(ulLen, lpProp, (void **)&lpProp->Value.MVbin.lpbin[ulMVProp].lpb);
  1116. if(hr != hrSuccess)
  1117. return hr;
  1118. memcpy(lpProp->Value.MVbin.lpbin[ulMVProp].lpb, lpBuffer, ulLen);
  1119. lpProp->Value.MVbin.lpbin[ulMVProp].cb = ulLen;
  1120. } else {
  1121. hr = MAPIAllocateMore(ulLen, lpProp, (void **)&lpProp->Value.bin.lpb);
  1122. if(hr != hrSuccess)
  1123. return hr;
  1124. memcpy(lpProp->Value.bin.lpb, lpBuffer, ulLen);
  1125. lpProp->Value.bin.cb = ulLen;
  1126. }
  1127. lpBuffer += ulLen;
  1128. ulSize -= ulLen;
  1129. // Re-align
  1130. lpBuffer += ulLen & 3 ? 4 - (ulLen & 3) : 0;
  1131. ulSize -= ulLen & 3 ? 4 - (ulLen & 3) : 0;
  1132. break;
  1133. case PT_CLSID:
  1134. if (ulSize < sizeof(GUID))
  1135. return MAPI_E_CORRUPT_DATA;
  1136. if(ulPropTag & MV_FLAG) {
  1137. memcpy(&lpProp->Value.MVguid.lpguid[ulMVProp], lpBuffer, sizeof(GUID));
  1138. } else {
  1139. hr = MAPIAllocateMore(sizeof(GUID), lpProp, (LPVOID*)&lpProp->Value.lpguid);
  1140. if (hr != hrSuccess)
  1141. return hr;
  1142. memcpy(lpProp->Value.lpguid, lpBuffer, sizeof(GUID));
  1143. }
  1144. lpBuffer += sizeof(GUID);
  1145. ulSize -= sizeof(GUID);
  1146. break;
  1147. default:
  1148. hr = MAPI_E_INVALID_PARAMETER;
  1149. break;
  1150. }
  1151. }
  1152. *lpulRead = ulOrigSize - ulSize;
  1153. *lppProp = lpProp.release();
  1154. return hr;
  1155. }
  1156. /**
  1157. * Add specified properties to the TNEF object list to save with
  1158. * Finish. This makes a lazy copy of the lpProps, so make sure you keep
  1159. * them in memory until you call Finish().
  1160. *
  1161. * @param[in] cValues Number of properties in lpProps.
  1162. * @param[in] lpProps Array of properties to add to the TNEF object
  1163. * @retval hrSuccess
  1164. */
  1165. HRESULT ECTNEF::SetProps(ULONG cValues, LPSPropValue lpProps)
  1166. {
  1167. unsigned int i = 0;
  1168. for (i = 0; i < cValues; ++i)
  1169. lstProps.push_back(&lpProps[i]);
  1170. return hrSuccess;
  1171. }
  1172. /**
  1173. * Add another component to the TNEF stream. This currently only works
  1174. * for attachments, and you have to pass the PR_ATTACH_NUM in
  1175. * 'ulComponentID'. We then serialize all properties passed in
  1176. * lpPropList into the TNEF stream. Currently we do NOT support
  1177. * ATTACH_EMBEDDED_MSG type attachments - this function is currently
  1178. * only really useful for ATTACH_OLE attachments.
  1179. *
  1180. * @param[in] ulFlags Must be TNEF_COMPONENT_ATTACHMENT, others currently not supported.
  1181. * @param[in] ulComponentID PR_ATTACH_NUM value passed to OpenAttachment()
  1182. * @param[in] lpPropList List of proptags to put in the TNEF stream of this attachment
  1183. * @return MAPI error code
  1184. */
  1185. HRESULT ECTNEF::FinishComponent(ULONG ulFlags, ULONG ulComponentID,
  1186. const SPropTagArray *lpPropList)
  1187. {
  1188. HRESULT hr = hrSuccess;
  1189. object_ptr<IAttach> lpAttach;
  1190. memory_ptr<SPropValue> lpProps, lpAttachProps, lpsNewProp;
  1191. object_ptr<IStream> lpStream;
  1192. ULONG cValues = 0;
  1193. AttachRendData sData;
  1194. static constexpr const SizedSPropTagArray(2, sptaTags) =
  1195. {2, {PR_ATTACH_METHOD, PR_RENDERING_POSITION}};
  1196. struct tnefattachment sTnefAttach;
  1197. if (ulFlags != TNEF_COMPONENT_ATTACHMENT)
  1198. return MAPI_E_NO_SUPPORT;
  1199. if (this->ulFlags != TNEF_ENCODE)
  1200. return MAPI_E_INVALID_PARAMETER;
  1201. hr = m_lpMessage->OpenAttach(ulComponentID, &IID_IAttachment, 0, &~lpAttach);
  1202. if(hr != hrSuccess)
  1203. return hr;
  1204. // Get some properties we always need
  1205. hr = lpAttach->GetProps(sptaTags, 0, &cValues, &~lpAttachProps);
  1206. if(FAILED(hr))
  1207. return hr;
  1208. // ignore warnings
  1209. hr = hrSuccess;
  1210. memset(&sData, 0, sizeof(sData));
  1211. sData.usType = lpAttachProps[0].ulPropTag == PR_ATTACH_METHOD && lpAttachProps[0].Value.ul == ATTACH_OLE ? AttachTypeOle : AttachTypeFile;
  1212. sData.ulPosition = lpAttachProps[1].ulPropTag == PR_RENDERING_POSITION ? lpAttachProps[1].Value.ul : 0;
  1213. // Get user-passed properties
  1214. hr = lpAttach->GetProps(lpPropList, 0, &cValues, &~lpProps);
  1215. if(FAILED(hr))
  1216. return hr;
  1217. for (unsigned int i = 0; i < cValues; ++i) {
  1218. // Other properties
  1219. if(PROP_TYPE(lpProps[i].ulPropTag) == PT_ERROR)
  1220. continue;
  1221. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpsNewProp);
  1222. if(hr != hrSuccess)
  1223. return hr;
  1224. if(PROP_TYPE(lpProps[i].ulPropTag) == PT_OBJECT) {
  1225. // PT_OBJECT requested, open object as stream and read the data
  1226. hr = lpAttach->OpenProperty(lpProps[i].ulPropTag, &IID_IStream, 0, 0, &~lpStream);
  1227. if(hr != hrSuccess)
  1228. return hr;
  1229. // We save the actual data same way as PT_BINARY
  1230. hr = HrReadStream(lpStream, lpsNewProp, &lpsNewProp->Value.bin.lpb, &lpsNewProp->Value.bin.cb);
  1231. if(hr != hrSuccess)
  1232. return hr;
  1233. lpsNewProp->ulPropTag = lpProps[i].ulPropTag;
  1234. } else {
  1235. hr = Util::HrCopyProperty(lpsNewProp, &lpProps[i], lpsNewProp);
  1236. if(hr != hrSuccess)
  1237. return hr;
  1238. }
  1239. sTnefAttach.lstProps.push_back(lpsNewProp.release());
  1240. }
  1241. sTnefAttach.rdata = sData;
  1242. sTnefAttach.data = NULL;
  1243. sTnefAttach.size = 0;
  1244. lstAttachments.push_back(new tnefattachment(sTnefAttach));
  1245. return hrSuccess;
  1246. }
  1247. /**
  1248. * Finalize the TNEF object. If the constructors ulFlags was
  1249. * TNEF_DECODE, the properties will be saved to the given message. If
  1250. * ulFlags was TNEF_ENCODE, the lpStream will be written the TNEF
  1251. * data.
  1252. *
  1253. * @return MAPI error code
  1254. */
  1255. HRESULT ECTNEF::Finish()
  1256. {
  1257. HRESULT hr = hrSuccess;
  1258. STATSTG sStat;
  1259. ULONG ulChecksum;
  1260. LARGE_INTEGER zero = {{0,0}};
  1261. ULARGE_INTEGER uzero = {{0,0}};
  1262. // attachment vars
  1263. ULONG ulAttachNum;
  1264. object_ptr<IStream> lpAttStream;
  1265. object_ptr<IMessage> lpAttMessage;
  1266. SPropValue sProp;
  1267. if(ulFlags == TNEF_DECODE) {
  1268. // Write properties to message
  1269. for (const auto p : lstProps) {
  1270. if (PROP_ID(p->ulPropTag) == PROP_ID(PR_MESSAGE_CLASS) ||
  1271. !FPropExists(m_lpMessage, p->ulPropTag) ||
  1272. PROP_ID(p->ulPropTag) == PROP_ID(PR_RTF_COMPRESSED) ||
  1273. PROP_ID(p->ulPropTag) == PROP_ID(PR_HTML) ||
  1274. PROP_ID(p->ulPropTag) == PROP_ID(PR_INTERNET_CPID))
  1275. m_lpMessage->SetProps(1, p, NULL);
  1276. // else, Property already exists, do *not* overwrite it
  1277. }
  1278. // Add all found attachments to message
  1279. for (const auto att : lstAttachments) {
  1280. object_ptr<IAttach> lpAttach;
  1281. bool has_obj = false;
  1282. hr = m_lpMessage->CreateAttach(nullptr, 0, &ulAttachNum, &~lpAttach);
  1283. if (hr != hrSuccess)
  1284. return hr;
  1285. sProp.ulPropTag = PR_ATTACH_METHOD;
  1286. if (att->rdata.usType == AttachTypeOle)
  1287. sProp.Value.ul = ATTACH_OLE;
  1288. else
  1289. sProp.Value.ul = ATTACH_BY_VALUE;
  1290. lpAttach->SetProps(1, &sProp, NULL);
  1291. sProp.ulPropTag = PR_RENDERING_POSITION;
  1292. sProp.Value.ul = att->rdata.ulPosition;
  1293. lpAttach->SetProps(1, &sProp, NULL);
  1294. for (const auto p : att->lstProps) {
  1295. // must not set PR_ATTACH_NUM by ourselves
  1296. if (PROP_ID(p->ulPropTag) == PROP_ID(PR_ATTACH_NUM))
  1297. continue;
  1298. if (p->ulPropTag != PR_ATTACH_DATA_OBJ) {
  1299. hr = lpAttach->SetProps(1, p, NULL);
  1300. if (hr != hrSuccess)
  1301. return hr;
  1302. } else {
  1303. // message in PT_OBJECT, was saved in Value.bin
  1304. if (att->rdata.usType == AttachTypeOle) {
  1305. object_ptr<IStream> lpSubStream;
  1306. hr = lpAttach->OpenProperty(p->ulPropTag, &IID_IStream, 0, MAPI_CREATE | MAPI_MODIFY, &~lpSubStream);
  1307. if(hr != hrSuccess)
  1308. return hr;
  1309. hr = lpSubStream->Write(p->Value.bin.lpb, p->Value.bin.cb, NULL);
  1310. if(hr != hrSuccess)
  1311. return hr;
  1312. hr = lpSubStream->Commit(0);
  1313. if(hr != hrSuccess)
  1314. return hr;
  1315. has_obj = true;
  1316. } else {
  1317. object_ptr<IStream> lpSubStream;
  1318. hr = CreateStreamOnHGlobal(nullptr, TRUE, &~lpSubStream);
  1319. if (hr != hrSuccess)
  1320. return hr;
  1321. hr = lpSubStream->Write(p->Value.bin.lpb, p->Value.bin.cb, NULL);
  1322. if (hr != hrSuccess)
  1323. return hr;
  1324. hr = lpSubStream->Seek(zero, STREAM_SEEK_SET, NULL);
  1325. if(hr != hrSuccess)
  1326. return hr;
  1327. hr = lpAttach->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY, &~lpAttMessage);
  1328. if(hr != hrSuccess)
  1329. return hr;
  1330. ECTNEF SubTNEF(TNEF_DECODE, lpAttMessage, lpSubStream);
  1331. hr = SubTNEF.ExtractProps(TNEF_PROP_EXCLUDE, NULL);
  1332. if (hr != hrSuccess)
  1333. return hr;
  1334. hr = SubTNEF.Finish();
  1335. if (hr != hrSuccess)
  1336. return hr;
  1337. hr = lpAttMessage->SaveChanges(0);
  1338. if (hr != hrSuccess)
  1339. return hr;
  1340. has_obj = true;
  1341. }
  1342. }
  1343. }
  1344. if (!has_obj && att->data != NULL) {
  1345. object_ptr<IStream> lpAttStream;
  1346. hr = lpAttach->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~lpAttStream);
  1347. if (hr != hrSuccess)
  1348. return hr;
  1349. hr = lpAttStream->Write(att->data, att->size,NULL);
  1350. if (hr != hrSuccess)
  1351. return hr;
  1352. hr = lpAttStream->Commit(0);
  1353. if (hr != hrSuccess)
  1354. return hr;
  1355. }
  1356. hr = lpAttach->SaveChanges(0);
  1357. if (hr != hrSuccess)
  1358. return hr;
  1359. }
  1360. } else if(ulFlags == TNEF_ENCODE) {
  1361. // Write properties to stream
  1362. hr = HrWriteDWord(m_lpStream, TNEF_SIGNATURE);
  1363. if(hr != hrSuccess)
  1364. return hr;
  1365. hr = HrWriteWord(m_lpStream, 0); // Write Key
  1366. if(hr != hrSuccess)
  1367. return hr;
  1368. hr = HrWriteByte(m_lpStream, 1); // Write component (always 1 ?)
  1369. if(hr != hrSuccess)
  1370. return hr;
  1371. object_ptr<IStream> lpPropStream;
  1372. hr = CreateStreamOnHGlobal(nullptr, TRUE, &~lpPropStream);
  1373. if(hr != hrSuccess)
  1374. return hr;
  1375. hr = HrWritePropStream(lpPropStream, lstProps);
  1376. if(hr != hrSuccess)
  1377. return hr;
  1378. hr = lpPropStream->Stat(&sStat, STATFLAG_NONAME);
  1379. if(hr != hrSuccess)
  1380. return hr;
  1381. hr = HrWriteDWord(m_lpStream, 0x00069003);
  1382. if(hr != hrSuccess)
  1383. return hr;
  1384. hr = HrWriteDWord(m_lpStream, sStat.cbSize.LowPart); // Write size
  1385. if(hr != hrSuccess)
  1386. return hr;
  1387. hr = lpPropStream->Seek(zero, STREAM_SEEK_SET, NULL);
  1388. if(hr != hrSuccess)
  1389. return hr;
  1390. hr = lpPropStream->CopyTo(m_lpStream, sStat.cbSize, NULL, NULL); // Write data
  1391. if(hr != hrSuccess)
  1392. return hr;
  1393. hr = lpPropStream->Seek(zero, STREAM_SEEK_SET, NULL);
  1394. if(hr != hrSuccess)
  1395. return hr;
  1396. hr = HrGetChecksum(lpPropStream, &ulChecksum);
  1397. if(hr != hrSuccess)
  1398. return hr;
  1399. hr = HrWriteWord(m_lpStream, (unsigned short)ulChecksum); // Write checksum
  1400. if(hr != hrSuccess)
  1401. return hr;
  1402. // Write attachments
  1403. for (const auto att : lstAttachments) {
  1404. /* Write attachment start block */
  1405. hr = HrWriteBlock(m_lpStream, reinterpret_cast<char *>(&att->rdata), sizeof(AttachRendData), 0x00069002, 2);
  1406. if(hr != hrSuccess)
  1407. return hr;
  1408. // Write attachment data block if available
  1409. if (att->data != NULL) {
  1410. hr = HrWriteBlock(m_lpStream, reinterpret_cast<char *>(att->data), att->size, 0x0006800f, 2);
  1411. if(hr != hrSuccess)
  1412. return hr;
  1413. }
  1414. // Write property block
  1415. hr = lpPropStream->SetSize(uzero);
  1416. if(hr != hrSuccess)
  1417. return hr;
  1418. hr = HrWritePropStream(lpPropStream, att->lstProps);
  1419. if(hr != hrSuccess)
  1420. return hr;
  1421. hr = HrWriteBlock(m_lpStream, lpPropStream, 0x00069005, 2);
  1422. if(hr != hrSuccess)
  1423. return hr;
  1424. // Note that we don't write any other blocks like PR_ATTACH_FILENAME since this information is also in the property block
  1425. }
  1426. }
  1427. return hrSuccess;
  1428. }
  1429. /**
  1430. * Read one DWORD (32-bit unsigned integer) from input stream
  1431. *
  1432. * @param[in] lpStream input stream to read one ULONG from, stream automatically moves current cursor.
  1433. * @param[out] ulData ULONG value from lpStream
  1434. * @retval MAPI_E_NOT_FOUND if stream was too short, other MAPI error code
  1435. * @return MAPI error code
  1436. */
  1437. HRESULT ECTNEF::HrReadDWord(IStream *lpStream, ULONG *ulData)
  1438. {
  1439. HRESULT hr;
  1440. ULONG ulRead = 0;
  1441. hr = lpStream->Read(ulData, sizeof(unsigned int), &ulRead);
  1442. if(hr != hrSuccess)
  1443. return hr;
  1444. if (ulRead != sizeof(unsigned int))
  1445. return MAPI_E_NOT_FOUND;
  1446. return hrSuccess;
  1447. }
  1448. /**
  1449. * Read one WORD (16-bit unsigned integer) from input stream
  1450. *
  1451. * @param[in] lpStream input stream to read one unsigned short from, stream automatically moves current cursor.
  1452. * @param[out] ulData unsigned short value from lpStream
  1453. * @retval MAPI_E_NOT_FOUND if stream was too short, other MAPI error code
  1454. * @return MAPI error code
  1455. */
  1456. HRESULT ECTNEF::HrReadWord(IStream *lpStream, unsigned short *ulData)
  1457. {
  1458. HRESULT hr;
  1459. ULONG ulRead = 0;
  1460. hr = lpStream->Read(ulData, sizeof(unsigned short), &ulRead);
  1461. if(hr != hrSuccess)
  1462. return hr;
  1463. if (ulRead != sizeof(unsigned short))
  1464. return MAPI_E_NOT_FOUND;
  1465. return hrSuccess;
  1466. }
  1467. /**
  1468. * Read one BYTE (CHAR_BIT-bits unsigned char) from input stream
  1469. *
  1470. * @param[in] lpStream input stream to read one unsigned char from, stream automatically moves current cursor.
  1471. * @param[out] ulData unsigned char value from lpStream
  1472. * @retval MAPI_E_NOT_FOUND if stream was too short, other MAPI error code
  1473. * @return MAPI error code
  1474. */
  1475. HRESULT ECTNEF::HrReadByte(IStream *lpStream, unsigned char *ulData)
  1476. {
  1477. HRESULT hr;
  1478. ULONG ulRead = 0;
  1479. hr = lpStream->Read(ulData, 1, &ulRead);
  1480. if(hr != hrSuccess)
  1481. return hr;
  1482. if (ulRead != 1)
  1483. return MAPI_E_NOT_FOUND;
  1484. return hrSuccess;
  1485. }
  1486. /**
  1487. * Read a block of data from the stream, with given length. Will be
  1488. * processes in blocks of 4096 bytes.
  1489. *
  1490. * @param[in] lpStream input stream to read one unsigned char from, stream automatically moves current cursor.
  1491. * @param[out] lpData pre-allocated buffer of size given in ulLen
  1492. * @param[in] ulLen Length to read from stream, and thus size of lpData
  1493. * @retval MAPI_E_NOT_FOUND if stream was too short, other MAPI error code
  1494. * @return MAPI error code
  1495. */
  1496. HRESULT ECTNEF::HrReadData(IStream *lpStream, char *lpData, ULONG ulLen)
  1497. {
  1498. HRESULT hr;
  1499. ULONG ulRead = 0;
  1500. ULONG ulToRead = 0;
  1501. while(ulLen) {
  1502. ulToRead = ulLen > 4096 ? 4096 : ulLen;
  1503. hr = lpStream->Read(lpData, ulToRead, &ulRead);
  1504. if(hr != hrSuccess)
  1505. return hr;
  1506. if (ulRead != ulToRead)
  1507. return MAPI_E_NOT_FOUND;
  1508. ulLen -= ulRead;
  1509. lpData += ulRead;
  1510. }
  1511. return hrSuccess;
  1512. }
  1513. /**
  1514. * Write one DWORD (32-bit integer) to output stream
  1515. *
  1516. * @param[in,out] lpStream stream to write one ULONG to, stream automatically moves current cursor.
  1517. * @param[in] ulData ULONG value to write in lpStream
  1518. * @retval MAPI_E_NOT_FOUND if stream was not written the same bytes, other MAPI error code
  1519. * @return MAPI error code
  1520. */
  1521. HRESULT ECTNEF::HrWriteDWord(IStream *lpStream, ULONG ulData)
  1522. {
  1523. HRESULT hr;
  1524. ULONG ulWritten = 0;
  1525. hr = lpStream->Write(&ulData, sizeof(unsigned int), &ulWritten);
  1526. if(hr != hrSuccess)
  1527. return hr;
  1528. if (ulWritten != sizeof(unsigned int))
  1529. return MAPI_E_NOT_FOUND;
  1530. return hrSuccess;
  1531. }
  1532. /**
  1533. * Write one WORD (16-bit unsigned integer) to output stream
  1534. *
  1535. * @param[in,out] lpStream stream to write one unsigned short to, stream automatically moves current cursor.
  1536. * @param[in] ulData unsigned short value to write in lpStream
  1537. * @retval MAPI_E_NOT_FOUND if stream was not written the same bytes, other MAPI error code
  1538. * @return MAPI error code
  1539. */
  1540. HRESULT ECTNEF::HrWriteWord(IStream *lpStream, unsigned short ulData)
  1541. {
  1542. HRESULT hr;
  1543. ULONG ulWritten = 0;
  1544. hr = lpStream->Write(&ulData, sizeof(unsigned short), &ulWritten);
  1545. if(hr != hrSuccess)
  1546. return hr;
  1547. if (ulWritten != sizeof(unsigned short))
  1548. return MAPI_E_NOT_FOUND;
  1549. return hrSuccess;
  1550. }
  1551. /**
  1552. * Write one BYTE (8-bit unsigned integer) to output stream
  1553. *
  1554. * @param[in,out] lpStream stream to write one unsigned char to, stream automatically moves current cursor.
  1555. * @param[in] ulData unsigned char value to write in lpStream
  1556. * @retval MAPI_E_NOT_FOUND if stream was not written the same bytes, other MAPI error code
  1557. * @return MAPI error code
  1558. */
  1559. HRESULT ECTNEF::HrWriteByte(IStream *lpStream, unsigned char ulData)
  1560. {
  1561. HRESULT hr;
  1562. ULONG ulWritten = 0;
  1563. hr = lpStream->Write(&ulData, sizeof(unsigned char), &ulWritten);
  1564. if(hr != hrSuccess)
  1565. return hr;
  1566. if (ulWritten != sizeof(unsigned char))
  1567. return MAPI_E_NOT_FOUND;
  1568. return hrSuccess;
  1569. }
  1570. /**
  1571. * Write a block of data of given size to output stream
  1572. *
  1573. * @param[in,out] lpStream stream to write one unsigned char to, stream automatically moves current cursor.
  1574. * @param[in] ulData unsigned char value to write in lpStream
  1575. * @return MAPI error code
  1576. */
  1577. HRESULT ECTNEF::HrWriteData(IStream *lpStream, const char *data, ULONG ulLen)
  1578. {
  1579. HRESULT hr;
  1580. ULONG ulWritten = 0;
  1581. while(ulLen > 0) {
  1582. hr = lpStream->Write(data, ulLen > 4096 ? 4096 : ulLen, &ulWritten);
  1583. if(hr != hrSuccess)
  1584. return hr;
  1585. ulLen -= ulWritten;
  1586. data += ulWritten;
  1587. }
  1588. return hrSuccess;
  1589. }
  1590. /**
  1591. * TNEF uses the rather stupid checksum of adding all the bytes in the stream.
  1592. * Was TNEF coded by an intern or something ??
  1593. *
  1594. * @param[in] lpStream Input TNEF stream, this object will be unmodified
  1595. * @param[out] lpulChecksum "Checksum" of the TNEF data
  1596. * @return MAPI error code
  1597. */
  1598. HRESULT ECTNEF::HrGetChecksum(IStream *lpStream, ULONG *lpulChecksum)
  1599. {
  1600. HRESULT hr = hrSuccess;
  1601. ULONG ulChecksum = 0;
  1602. object_ptr<IStream> lpClone;
  1603. LARGE_INTEGER zero = {{0,0}};
  1604. ULONG ulRead = 0;
  1605. unsigned char buffer[4096];
  1606. unsigned int i = 0;
  1607. hr = lpStream->Clone(&~lpClone);
  1608. if(hr != hrSuccess)
  1609. return hr;
  1610. hr = lpClone->Seek(zero, STREAM_SEEK_SET, NULL);
  1611. if(hr != hrSuccess)
  1612. return hr;
  1613. while(TRUE) {
  1614. hr = lpClone->Read(buffer, 4096, &ulRead);
  1615. if(hr != hrSuccess)
  1616. return hr;
  1617. if(ulRead == 0)
  1618. break;
  1619. for (i = 0; i < ulRead; ++i)
  1620. ulChecksum += buffer[i];
  1621. }
  1622. *lpulChecksum = ulChecksum;
  1623. return hrSuccess;
  1624. }
  1625. /**
  1626. * Create a TNEF checksum over a normal char buffer.
  1627. *
  1628. * @param[in] lpData Buffer containing TNEF data
  1629. * @param[in] ulLen Length of lpData
  1630. * @return TNEF checksum value
  1631. */
  1632. ULONG ECTNEF::GetChecksum(const char *lpData, unsigned int ulLen) const
  1633. {
  1634. ULONG ulChecksum = 0;
  1635. for (unsigned int i = 0; i < ulLen; ++i)
  1636. ulChecksum += lpData[i];
  1637. return ulChecksum;
  1638. }
  1639. /**
  1640. * Copy stream data to another stream with given TNEF block id and level number.
  1641. *
  1642. * @param[in,out] lpDestStream Stream to write data to
  1643. * @param[in] lpSourceStream Stream to read data from
  1644. * @param[in] ulBlockID TNEF block id number
  1645. * @param[in] ulLevel TNEF level number
  1646. *
  1647. * @return MAPI error code
  1648. */
  1649. HRESULT ECTNEF::HrWriteBlock(IStream *lpDestStream, IStream *lpSourceStream, ULONG ulBlockID, ULONG ulLevel)
  1650. {
  1651. HRESULT hr;
  1652. ULONG ulChecksum = 0;
  1653. LARGE_INTEGER zero = {{0,0}};
  1654. STATSTG sStat;
  1655. hr = HrWriteByte(lpDestStream, ulLevel);
  1656. if(hr != hrSuccess)
  1657. return hr;
  1658. hr = HrGetChecksum(lpSourceStream, &ulChecksum);
  1659. if(hr != hrSuccess)
  1660. return hr;
  1661. hr = lpSourceStream->Seek(zero, STREAM_SEEK_SET, NULL);
  1662. if(hr != hrSuccess)
  1663. return hr;
  1664. hr = HrWriteDWord(lpDestStream, ulBlockID);
  1665. if(hr != hrSuccess)
  1666. return hr;
  1667. hr = lpSourceStream->Stat(&sStat, STATFLAG_NONAME);
  1668. if(hr != hrSuccess)
  1669. return hr;
  1670. hr = HrWriteDWord(lpDestStream, sStat.cbSize.QuadPart);
  1671. if(hr != hrSuccess)
  1672. return hr;
  1673. hr = lpSourceStream->CopyTo(lpDestStream, sStat.cbSize, NULL, NULL);
  1674. if(hr != hrSuccess)
  1675. return hr;
  1676. hr = HrWriteWord(lpDestStream, ulChecksum);
  1677. if(hr != hrSuccess)
  1678. return hr;
  1679. return hrSuccess;
  1680. }
  1681. /**
  1682. * Write a buffer to a stream with given TNEF block id and level number.
  1683. *
  1684. * @param[in,out] lpDestStream Stream to write data block in
  1685. * @param[in] lpData Data block to write to stream
  1686. * @param[in] ulLen Lenght of lpData
  1687. * @param[in] ulBlockID TNEF Block ID number
  1688. * @param[in] ulLevel TNEF Level number
  1689. *
  1690. * @return MAPI error code
  1691. */
  1692. HRESULT ECTNEF::HrWriteBlock(IStream *lpDestStream, const char *lpData,
  1693. unsigned int ulLen, ULONG ulBlockID, ULONG ulLevel)
  1694. {
  1695. HRESULT hr = hrSuccess;
  1696. object_ptr<IStream> lpStream;
  1697. hr = CreateStreamOnHGlobal(nullptr, TRUE, &~lpStream);
  1698. if (hr != hrSuccess)
  1699. return hr;
  1700. hr = lpStream->Write(lpData, ulLen, NULL);
  1701. if (hr != hrSuccess)
  1702. return hr;
  1703. return HrWriteBlock(lpDestStream, lpStream, ulBlockID, ulLevel);
  1704. }
  1705. /**
  1706. * Read a complete stream into a buffer. (Don't we have this function somewhere in common/ ?)
  1707. *
  1708. * @param[in] lpStream stream to read into buffer and return as BYTE array, cursor will be at the end on return
  1709. * @param[in] lpBase pointer to use with MAPIAllocateMore, cannot be NULL
  1710. * @param[out] lppData New allocated (more) buffer with contents of stream
  1711. * @param[out] lpulSize size of *lppData buffer
  1712. *
  1713. * @return MAPI error code
  1714. */
  1715. HRESULT ECTNEF::HrReadStream(IStream *lpStream, void *lpBase, BYTE **lppData, ULONG *lpulSize)
  1716. {
  1717. HRESULT hr;
  1718. STATSTG sStat;
  1719. BYTE *lpBuffer = NULL;
  1720. BYTE *lpWrite = NULL;
  1721. ULONG ulSize = 0;
  1722. ULONG ulRead = 0;
  1723. if (lpStream == NULL || lpBase == NULL || lppData == NULL || lpulSize == NULL)
  1724. return MAPI_E_INVALID_PARAMETER;
  1725. hr = lpStream->Stat(&sStat, STATFLAG_NONAME);
  1726. if(hr != hrSuccess)
  1727. return hr;
  1728. hr = MAPIAllocateMore(sStat.cbSize.QuadPart, lpBase, (void **)&lpBuffer);
  1729. if(hr != hrSuccess)
  1730. return hr;
  1731. lpWrite = lpBuffer;
  1732. while(sStat.cbSize.QuadPart > 0) {
  1733. hr = lpStream->Read(lpWrite, sStat.cbSize.QuadPart, &ulRead);
  1734. if(hr != hrSuccess)
  1735. return hr;
  1736. lpWrite += ulRead;
  1737. ulSize += ulRead;
  1738. sStat.cbSize.QuadPart -= ulRead;
  1739. }
  1740. *lppData = lpBuffer;
  1741. *lpulSize = ulSize;
  1742. return hrSuccess;
  1743. }
  1744. } /* namespace */
  1745. /** @} */