12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- /**
- * @defgroup tnef TNEF reader and writer
- * @{
- */
- /**
- * @brief
- * This is our TNEF class, which has been specially designed for
- * simple TNEF reading and writing.
- *
- * Currently does not support recipient-table properties.
- *
- * TNEF has gone through various versions for Microsoft Mail and
- * other really old systems, and therefore has an elaborate backwards-
- * compatibility system. This means that most properties can be stored
- * in both TNEF properties as within a single 'MAPI' property of the
- * TNEF stream (0x00069003). We basically discard all the backwards-
- * compatibility and write TNEF streams that only work with
- * Outlook 2000 or later (maybe also Outlook 97, not sure about that)
- * by only writing the TNEF stream properties in 0x00069003.
- *
- * -- Steve
- */
-
- #include <kopano/platform.h>
- #include <memory>
- #include <mapidefs.h>
- #include <mapiutil.h>
- #include <mapiguid.h>
- #include <kopano/mapiext.h>
- #include <kopano/memory.hpp>
- #include <kopano/Util.h>
- #include <kopano/charset/convert.h>
- #include <string>
- #include "tnef.h"
- using namespace KCHL;
- namespace KC {
- enum {
- ATT_ATTACH_TITLE = 0x18010,
- ATT_REQUEST_RES = 0x40009,
- ATT_ATTACH_DATA = 0x6800F,
- ATT_ATTACH_META_FILE = 0x68011,
- ATT_ATTACH_REND_DATA = 0x69002,
- ATT_MAPI_PROPS = 0x69003,
- ATT_ATTACHMENT = 0x69005,
- ATT_MESSAGE_CLASS = 0x78008,
- };
- // The mapping between Microsoft Mail IPM classes and those used in MAPI
- // see: http://msdn2.microsoft.com/en-us/library/ms527360.aspx
- static const struct _sClassMap {
- const char *szScheduleClass;
- const char *szMAPIClass;
- } sClassMap[] = {
- { "IPM.Microsoft Schedule.MtgReq", "IPM.Schedule.Meeting.Request" },
- { "IPM.Microsoft Schedule.MtgRespP", "IPM.Schedule.Meeting.Resp.Pos" },
- { "IPM.Microsoft Schedule.MtgRespN", "IPM.Schedule.Meeting.Resp.Neg" },
- { "IPM.Microsoft Schedule.MtgRespA", "IPM.Schedule.Meeting.Resp.Tent" },
- { "IPM.Microsoft Schedule.MtgCncl", "IPM.Schedule.Meeting.Canceled" },
- { "IPM.Microsoft Mail.Non-Delivery", "Report.IPM.Note.NDR" },
- { "IPM.Microsoft Mail.Read Receipt", "Report.IPM.Note.IPNRN" },
- { "IPM.Microsoft Mail.Note", "IPM.Note" },
- { "IPM.Microsoft Mail.Note", "IPM" }
- };
- static const char *FindMAPIClassByScheduleClass(const char *szSClass)
- {
- for (size_t i = 0; i < ARRAY_SIZE(sClassMap); ++i)
- if(strcasecmp(szSClass, sClassMap[i].szScheduleClass) == 0) {
- return sClassMap[i].szMAPIClass;
- }
- return NULL;
- }
- /**
- * Returns TRUE if the given property tag is in the given property tag array
- *
- * @param[in] ulPropTag The property tag to find in lpPropList
- * @param[in] lpPropList The proptagarray to loop through
- * @retval true ulPropTag is alread present in lpPropList
- * @retval false ulPropTag is not present in lpPropList
- */
- static bool PropTagInPropList(ULONG ulPropTag, const SPropTagArray *lpPropList)
- {
- if (lpPropList == NULL)
- return false;
- for (ULONG i = 0; i < lpPropList->cValues; ++i)
- if (PROP_ID(ulPropTag) == PROP_ID(lpPropList->aulPropTag[i]))
- return true;
- return false;
- }
- /**
- * ECTNEF constructor, used for base and sub objects in TNEF streams
- *
- * @param[in] ulFlags TNEF_ENCODE
- * @param[in] lpMessage Properties from this message will be saved to lpStream as TNEF data
- * @param[in,out] lpStream An existing empty stream to save the propteries to as TNEF data
- *
- * @param[in] ulFlags TNEF_DECODE
- * @param[in,out] lpMessage TNEF properties will be saved to this message, and attachments will be create under this message.
- * @param[in] lpStream IStream object to the TNEF data
- */
- ECTNEF::ECTNEF(ULONG ulFlags, IMessage *lpMessage, IStream *lpStream) :
- m_lpStream(lpStream), m_lpMessage(lpMessage)
- {
- this->ulFlags = ulFlags;
- }
- /**
- * ECTNEF destructor frees allocated memory while handling the TNEF
- * stream.
- */
- ECTNEF::~ECTNEF()
- {
- for (const auto p : lstProps)
- MAPIFreeBuffer(p);
- for (const auto a : lstAttachments)
- FreeAttachmentData(a);
- }
- /**
- * Frees all allocated memory for attachments found in the TNEF
- * stream.
- *
- * @param[in,out] lpTnefAtt free all data in this attachment and delete the pointer too
- */
- void ECTNEF::FreeAttachmentData(tnefattachment* lpTnefAtt)
- {
- delete[] lpTnefAtt->data;
- for (const auto p : lpTnefAtt->lstProps)
- MAPIFreeBuffer(p);
- delete lpTnefAtt;
- }
- /**
- * Read data from lpStream and set in memory as one large
- * property. Only used to save MAPI_E_NOT_ENOUGH_MEMORY properties
- * from m_lpMessage to a separate LPSPropValue which will be saved in
- * the TNEF Stream later in Finish().
- *
- * @param[in] lpStream Input stream that points to PT_BINARY or PT_UNICODE data
- * @param[in] ulPropTag Current data type of lpStream, TYPE part can only contain either PT_BINARY or PT_UNICODE.
- * @param[out] lppPropValue Property structure to return data from stream in, with ulPropTag
- * @return MAPI error code, stream errors, memory errors.
- * @retval MAPI_E_INVALID_PARAMETER invalid lpStream of lppPropValue pointer
- * @retval MAPI_E_INVALID_TYPE invalid ulPropTag
- */
- static HRESULT StreamToPropValue(IStream *lpStream, ULONG ulPropTag,
- LPSPropValue *lppPropValue)
- {
- HRESULT hr = hrSuccess;
- memory_ptr<SPropValue> lpPropValue;
- STATSTG sStatstg;
- ULONG ulRead = 0;
- ULONG ulTotal = 0;
- BYTE *wptr = NULL;
- if (lpStream == NULL || lppPropValue == NULL)
- return MAPI_E_INVALID_PARAMETER;
- if (PROP_TYPE(ulPropTag) != PT_BINARY && PROP_TYPE(ulPropTag) != PT_UNICODE)
- return MAPI_E_INVALID_TYPE;
- hr = lpStream->Stat(&sStatstg, 0);
- if(hr != hrSuccess)
- return hr;
- hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpPropValue);
- if(hr != hrSuccess)
- return hr;
- lpPropValue->ulPropTag = ulPropTag;
-
- if (PROP_TYPE(ulPropTag) == PT_BINARY) {
- lpPropValue->Value.bin.cb = (ULONG)sStatstg.cbSize.QuadPart;
-
- hr = MAPIAllocateMore((ULONG)sStatstg.cbSize.QuadPart, lpPropValue, (void**)&lpPropValue->Value.bin.lpb);
- if(hr != hrSuccess)
- return hr;
- wptr = lpPropValue->Value.bin.lpb;
- } else if (PROP_TYPE(ulPropTag) == PT_UNICODE) {
- hr = MAPIAllocateMore((ULONG)sStatstg.cbSize.QuadPart + sizeof(WCHAR), lpPropValue, (void**)&lpPropValue->Value.lpszW);
- if (hr != hrSuccess)
- return hr;
- // terminate unicode string
- lpPropValue->Value.lpszW[sStatstg.cbSize.QuadPart / sizeof(WCHAR)] = L'\0';
- wptr = (BYTE*)lpPropValue->Value.lpszW;
- }
- while (1) {
- hr = lpStream->Read(wptr + ulTotal, 4096, &ulRead);
- if (hr != hrSuccess)
- return hr;
- if (ulRead == 0)
- break;
- ulTotal += ulRead;
- }
- *lppPropValue = lpPropValue.release();
- return hrSuccess;
- }
- /**
- * Adds the requested properties from the message into the pending
- * TNEF stream. String properties in lpPropList must be in
- * PT_UNICODE. PT_STRING8 properties will never be added.
- *
- * @param[in] ulFlags TNEF_PROP_INCLUDE: add only properties from message to stream from the lpPropList, or
- * TNEF_PROP_EXCLUDE: add all properties except if listed in lpPropList
- * @param[in] lpPropList List of properties to add to the stream if present in m_lpMessage, or
- * List of properties to exclude from the message
- * @return MAPI error code
- */
- HRESULT ECTNEF::AddProps(ULONG ulFlags, const SPropTagArray *lpPropList)
- {
- HRESULT hr = hrSuccess;
- memory_ptr<SPropTagArray> lpPropListMessage;
- memory_ptr<SPropValue> lpPropValue;
- LPSPropValue lpStreamValue = NULL;
- SizedSPropTagArray(1, sPropTagArray);
- unsigned int i = 0;
- bool fPropTagInList = false;
- ULONG cValue = 0;
- // Loop through all the properties on the message, and only
- // add those that we want to add to the list
- hr = m_lpMessage->GetPropList(MAPI_UNICODE, &~lpPropListMessage);
- if (hr != hrSuccess)
- return hr;
- for (i = 0; i < lpPropListMessage->cValues; ++i) {
- /*
- * Do not send properties in 0x67XX range, since these seem to
- * be blacklisted in recent exchange servers, which causes
- * exchange to drop the entire message.
- */
- if (PROP_ID(lpPropListMessage->aulPropTag[i]) >= 0x6700 &&
- PROP_ID(lpPropListMessage->aulPropTag[i]) <= 0x67FF)
- continue;
- // unable to save these properties
- if(PROP_TYPE(lpPropListMessage->aulPropTag[i]) == PT_OBJECT ||
- PROP_TYPE(lpPropListMessage->aulPropTag[i]) == PT_UNSPECIFIED ||
- PROP_TYPE(lpPropListMessage->aulPropTag[i]) == PT_NULL)
- continue;
- fPropTagInList = PropTagInPropList(lpPropListMessage->aulPropTag[i], lpPropList);
- bool a = ulFlags & TNEF_PROP_INCLUDE && fPropTagInList;
- a |= ulFlags & TNEF_PROP_EXCLUDE && !fPropTagInList;
- if (!a)
- continue;
- sPropTagArray.cValues = 1;
- sPropTagArray.aulPropTag[0] = lpPropListMessage->aulPropTag[i];
- hr = m_lpMessage->GetProps(sPropTagArray, 0, &cValue, &~lpPropValue);
- if (hr == hrSuccess)
- lstProps.push_back(lpPropValue.release());
- object_ptr<IStream> lpStream;
- if (hr == MAPI_W_ERRORS_RETURNED && lpPropValue != NULL &&
- lpPropValue->Value.err == MAPI_E_NOT_ENOUGH_MEMORY &&
- m_lpMessage->OpenProperty(lpPropListMessage->aulPropTag[i], &IID_IStream, 0, 0, &~lpStream) == hrSuccess) {
- hr = StreamToPropValue(lpStream, lpPropListMessage->aulPropTag[i], &lpStreamValue);
- if (hr == hrSuccess) {
- lstProps.push_back(lpStreamValue);
- lpStreamValue = NULL;
- }
- }
- // otherwise silently ignore the property
- }
- return hrSuccess;
- }
- /**
- * Extracts the properties from the TNEF stream, and sets them in the message
- *
- * @param[in] ulFlags TNEF_PROP_INCLUDE or TNEF_PROP_EXCLUDE
- * @param[in] lpPropList List of properties to include from the stream if present in m_lpMessage or
- * List of properties to exclude from the stream if present in m_lpMessage
- *
- * @retval MAPI_E_CORRUPT_DATA TNEF stream input is broken, or other MAPI error codes
- */
- HRESULT ECTNEF::ExtractProps(ULONG ulFlags, LPSPropTagArray lpPropList)
- {
- HRESULT hr = hrSuccess;
- ULONG ulSignature = 0;
- ULONG ulType = 0;
- ULONG ulSize = 0;
- unsigned short ulChecksum = 0;
- unsigned short ulKey = 0;
- unsigned char ulComponent = 0;
- memory_ptr<char> lpBuffer;
- SPropValue sProp;
- std::unique_ptr<char[]> szSClass;
- // Attachments props
- memory_ptr<SPropValue> lpProp;
- tnefattachment* lpTnefAtt = NULL;
- hr = HrReadDWord(m_lpStream, &ulSignature);
- if(hr != hrSuccess)
- goto exit;
- // Check signature
- if(ulSignature != TNEF_SIGNATURE) {
- hr = MAPI_E_CORRUPT_DATA;
- goto exit;
- }
- hr = HrReadWord(m_lpStream, &ulKey);
- if(hr != hrSuccess)
- goto exit;
- // File is made of blocks, with each a type and size. Component and Key are ignored.
- while(1) {
- hr = HrReadByte(m_lpStream, &ulComponent);
- if(hr != hrSuccess) {
- hr = hrSuccess; // EOF -> no error
- goto exit;
- }
- hr = HrReadDWord(m_lpStream, &ulType);
- if(hr != hrSuccess)
- goto exit;
- hr = HrReadDWord(m_lpStream, &ulSize);
- if(hr != hrSuccess)
- goto exit;
- if (ulSize == 0) {
- // do not allocate 0 size data block
- hr = MAPI_E_CORRUPT_DATA;
- goto exit;
- }
- hr = MAPIAllocateBuffer(ulSize, &~lpBuffer);
- if(hr != hrSuccess)
- goto exit;
- hr = HrReadData(m_lpStream, lpBuffer, ulSize);
- if(hr != hrSuccess)
- goto exit;
- hr = HrReadWord(m_lpStream, &ulChecksum);
- if(hr != hrSuccess)
- goto exit;
- // Loop through all the blocks of the TNEF data. We are only interested
- // in the properties block for now (0x00069003)
- switch(ulType) {
- case ATT_MAPI_PROPS:
- hr = HrReadPropStream(lpBuffer, ulSize, lstProps);
- if (hr != hrSuccess)
- goto exit;
- break;
- case ATT_MESSAGE_CLASS: /* PR_MESSAGE_CLASS */
- {
- szSClass.reset(new char[ulSize+1]);
- char *szMAPIClass = NULL;
- // NULL terminate the string
- memcpy(szSClass.get(), lpBuffer, ulSize);
- szSClass[ulSize] = 0;
- // We map the Schedule+ message class to the more modern MAPI message
- // class. The mapping should be correct as far as we can find ..
- szMAPIClass = (char *)FindMAPIClassByScheduleClass(szSClass.get());
- if(szMAPIClass == NULL)
- szMAPIClass = szSClass.get(); // mapping not found, use string from TNEF file
- sProp.ulPropTag = PR_MESSAGE_CLASS_A;
- sProp.Value.lpszA = szMAPIClass;
- // We do a 'SetProps' now because we want to override the PR_MESSAGE_CLASS
- // setting, while Finish() never overrides already-present properties for
- // security reasons.
- m_lpMessage->SetProps(1, &sProp, NULL);
- break;
- }
- case 0x00050008: /* PR_OWNER_APPT_ID */
- if(ulSize == 4 && lpBuffer) {
- sProp.ulPropTag = PR_OWNER_APPT_ID;
- sProp.Value.l = *reinterpret_cast<LONG *>(lpBuffer.get());
- m_lpMessage->SetProps(1, &sProp, NULL);
- }
- break;
- case ATT_REQUEST_RES: /* PR_RESPONSE_REQUESTED */
- if(ulSize == 2 && lpBuffer) {
- sProp.ulPropTag = PR_RESPONSE_REQUESTED;
- sProp.Value.b = static_cast<bool>(*reinterpret_cast<short *>(lpBuffer.get()));
- m_lpMessage->SetProps(1, &sProp, NULL);
- }
- break;
- // --- TNEF attachemnts ---
- case ATT_ATTACH_REND_DATA:
- // Start marker of attachment
- if(ulSize == sizeof(struct AttachRendData) && lpBuffer) {
- auto lpData = reinterpret_cast<AttachRendData *>(lpBuffer.get());
-
- if (lpTnefAtt) {
- if (lpTnefAtt->data || !lpTnefAtt->lstProps.empty()) // end marker previous attachment
- lstAttachments.push_back(lpTnefAtt);
- else
- FreeAttachmentData(lpTnefAtt);
- }
- lpTnefAtt = new tnefattachment;
- lpTnefAtt->size = 0;
- lpTnefAtt->data = NULL;
- lpTnefAtt->rdata = *lpData;
- }
- break;
- case ATT_ATTACH_TITLE: // PR_ATTACH_FILENAME
- if (!lpTnefAtt) {
- hr = MAPI_E_CORRUPT_DATA;
- goto exit;
- }
- hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpProp);
- if (hr != hrSuccess)
- goto exit;
- lpProp->ulPropTag = PR_ATTACH_FILENAME_A;
- if ((hr = MAPIAllocateMore(ulSize, lpProp, (void**)&lpProp->Value.lpszA)) != hrSuccess)
- goto exit;
- memcpy(lpProp->Value.lpszA, lpBuffer, ulSize);
- lpTnefAtt->lstProps.push_back(lpProp.release());
- break;
- case ATT_ATTACH_META_FILE:
- // PR_ATTACH_RENDERING, extra icon information
- if (!lpTnefAtt) {
- hr = MAPI_E_CORRUPT_DATA;
- goto exit;
- }
- hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpProp);
- if (hr != hrSuccess)
- goto exit;
- lpProp->ulPropTag = PR_ATTACH_RENDERING;
- if ((hr = MAPIAllocateMore(ulSize, lpProp, (void**)&lpProp->Value.bin.lpb)) != hrSuccess)
- goto exit;
- lpProp->Value.bin.cb = ulSize;
- memcpy(lpProp->Value.bin.lpb, lpBuffer, ulSize);
- lpTnefAtt->lstProps.push_back(lpProp.release());
- break;
- case ATT_ATTACH_DATA:
- // PR_ATTACH_DATA_BIN, will be set via OpenProperty() in ECTNEF::Finish()
- if (!lpTnefAtt) {
- hr = MAPI_E_CORRUPT_DATA;
- goto exit;
- }
- lpTnefAtt->size = ulSize;
- lpTnefAtt->data = new BYTE[ulSize];
- memcpy(lpTnefAtt->data, lpBuffer, ulSize);
- break;
- case ATT_ATTACHMENT: // Attachment property stream
- if (!lpTnefAtt) {
- hr = MAPI_E_CORRUPT_DATA;
- goto exit;
- }
- hr = HrReadPropStream(lpBuffer, ulSize, lpTnefAtt->lstProps);
- if (hr != hrSuccess)
- goto exit;
- break;
- default:
- // Ignore this block
- break;
- }
- }
- exit:
- if (lpTnefAtt) {
- if (lpTnefAtt->data || !lpTnefAtt->lstProps.empty()) // attachment should be complete before adding
- lstAttachments.push_back(lpTnefAtt);
- else
- FreeAttachmentData(lpTnefAtt);
- }
- return hr;
- }
- /**
- * Write the properties from a list to the TNEF stream.
- *
- * @param[in,out] lpStream The TNEF stream to write to
- * @param[in] proplist std::list of properties to write in the stream.
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrWritePropStream(IStream *lpStream, std::list<SPropValue *> &proplist)
- {
- HRESULT hr = HrWriteDWord(lpStream, proplist.size());
- if(hr != hrSuccess)
- return hr;
- for (const auto p : proplist) {
- hr = HrWriteSingleProp(lpStream, p);
- if (hr != hrSuccess)
- return hr;
- }
- return hr;
- }
- /**
- * Write one property to the TNEF stream.
- *
- * @param[in,out] lpStream The TNEF stream to write to
- * @param[in] lpProp MAPI property to write to the TNEF stream
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrWriteSingleProp(IStream *lpStream, LPSPropValue lpProp)
- {
- HRESULT hr = hrSuccess;
- SizedSPropTagArray(1, sPropTagArray);
- ULONG cNames = 0;
- memory_ptr<MAPINAMEID *> lppNames;
- ULONG ulLen = 0;
- ULONG ulMVProp = 0;
- ULONG ulCount = 0;
- convert_context converter;
- std::u16string ucs2;
- if(PROP_ID(lpProp->ulPropTag) >= 0x8000) {
- memory_ptr<SPropTagArray> lpsPropTagArray;
- // Get named property GUID and ID or name
- sPropTagArray.cValues = 1;
- sPropTagArray.aulPropTag[0] = lpProp->ulPropTag;
- hr = Util::HrCopyPropTagArray(sPropTagArray, &~lpsPropTagArray);
- if (hr != hrSuccess)
- return hr;
- hr = m_lpMessage->GetNamesFromIDs(&+lpsPropTagArray, NULL, 0, &cNames, &~lppNames);
- if(hr != hrSuccess)
- return hrSuccess;
- if (cNames == 0 || lppNames == nullptr || lppNames[0] == nullptr)
- return MAPI_E_INVALID_PARAMETER;
- // Write the property tag
- hr = HrWriteDWord(lpStream, lpProp->ulPropTag);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteData(lpStream, (char *)lppNames[0]->lpguid, sizeof(GUID));
- if(hr != hrSuccess)
- return hr;
- if(lppNames[0]->ulKind == MNID_ID) {
- hr = HrWriteDWord(lpStream, 0);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, lppNames[0]->Kind.lID);
- if(hr != hrSuccess)
- return hr;
- } else {
- hr = HrWriteDWord(lpStream, 1);
- if(hr != hrSuccess)
- return hr;
- ucs2 = converter.convert_to<std::u16string>(lppNames[0]->Kind.lpwstrName);
- ulLen = ucs2.length() * sizeof(std::u16string::value_type) + sizeof(std::u16string::value_type);
- hr = HrWriteDWord(lpStream, ulLen);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteData(lpStream, (char *)ucs2.c_str(), ulLen);
- if(hr != hrSuccess)
- return hr;
- // Align to 4-byte boundary
- while(ulLen & 3) {
- hr = HrWriteByte(lpStream, 0);
- if(hr != hrSuccess)
- return hr;
- ++ulLen;
- }
- }
- } else {
- // Write the property tag
- hr = HrWriteDWord(lpStream, lpProp->ulPropTag);
- if(hr != hrSuccess)
- return hr;
- }
- // Now, write the actual property value
- if(PROP_TYPE(lpProp->ulPropTag) & MV_FLAG) {
- switch(PROP_TYPE(lpProp->ulPropTag)) {
- case PT_MV_I2:
- ulCount = lpProp->Value.MVi.cValues;
- break;
- case PT_MV_LONG:
- ulCount = lpProp->Value.MVl.cValues;
- break;
- case PT_MV_R4:
- ulCount = lpProp->Value.MVflt.cValues;
- break;
- case PT_MV_APPTIME:
- ulCount = lpProp->Value.MVat.cValues;
- break;
- case PT_MV_DOUBLE:
- ulCount = lpProp->Value.MVdbl.cValues;
- break;
- case PT_MV_CURRENCY:
- ulCount = lpProp->Value.MVcur.cValues;
- break;
- case PT_MV_SYSTIME:
- ulCount = lpProp->Value.MVft.cValues;
- break;
- case PT_MV_I8:
- ulCount = lpProp->Value.MVli.cValues;
- break;
- case PT_MV_STRING8:
- ulCount = lpProp->Value.MVszA.cValues;
- break;
- case PT_MV_UNICODE:
- ulCount = lpProp->Value.MVszW.cValues;
- break;
- case PT_MV_BINARY:
- ulCount = lpProp->Value.MVbin.cValues;
- break;
- case PT_MV_CLSID:
- ulCount = lpProp->Value.MVguid.cValues;
- break;
- default:
- return MAPI_E_INVALID_PARAMETER;
- }
- hr = HrWriteDWord(lpStream, ulCount);
- } else {
- ulCount = 1;
- }
- ulMVProp = 0;
- for (ulMVProp = 0; ulMVProp < ulCount; ++ulMVProp) {
- switch(PROP_TYPE(lpProp->ulPropTag) &~ MV_FLAG) {
- case PT_I2:
- if(lpProp->ulPropTag & MV_FLAG)
- hr = HrWriteDWord(lpStream,lpProp->Value.MVi.lpi[ulMVProp]);
- else
- hr = HrWriteDWord(lpStream,lpProp->Value.i);
- break;
- case PT_LONG:
- if(lpProp->ulPropTag & MV_FLAG)
- hr = HrWriteDWord(lpStream,lpProp->Value.MVl.lpl[ulMVProp]);
- else
- hr = HrWriteDWord(lpStream,lpProp->Value.ul);
- break;
- case PT_BOOLEAN:
- hr = HrWriteDWord(lpStream, lpProp->Value.b);
- break;
- case PT_R4:
- if(lpProp->ulPropTag & MV_FLAG)
- hr = HrWriteData(lpStream,(char *)&lpProp->Value.MVflt.lpflt[ulMVProp], sizeof(float));
- else
- hr = HrWriteData(lpStream,(char *)&lpProp->Value.flt, sizeof(float));
- break;
- case PT_APPTIME:
- if(lpProp->ulPropTag & MV_FLAG)
- hr = HrWriteData(lpStream,(char *)&lpProp->Value.MVat.lpat[ulMVProp], sizeof(double));
- else
- hr = HrWriteData(lpStream,(char *)&lpProp->Value.at, sizeof(double));
- break;
- case PT_DOUBLE:
- if(lpProp->ulPropTag & MV_FLAG)
- hr = HrWriteData(lpStream,(char *)&lpProp->Value.MVdbl.lpdbl[ulMVProp], sizeof(double));
- else
- hr = HrWriteData(lpStream,(char *)&lpProp->Value.dbl, sizeof(double));
- break;
- case PT_CURRENCY:
- if(lpProp->ulPropTag & MV_FLAG) {
- hr = HrWriteDWord(lpStream, lpProp->Value.MVcur.lpcur[ulMVProp].Lo);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, lpProp->Value.MVcur.lpcur[ulMVProp].Hi);
- } else {
- hr = HrWriteDWord(lpStream, lpProp->Value.cur.Lo);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, lpProp->Value.cur.Hi);
- }
- if (hr != hrSuccess)
- return hr;
- break;
- case PT_SYSTIME:
- if(lpProp->ulPropTag & MV_FLAG) {
- hr = HrWriteDWord(lpStream, lpProp->Value.MVft.lpft[ulMVProp].dwLowDateTime);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, lpProp->Value.MVft.lpft[ulMVProp].dwHighDateTime);
- } else {
- hr = HrWriteDWord(lpStream, lpProp->Value.ft.dwLowDateTime);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, lpProp->Value.ft.dwHighDateTime);
- }
- if (hr != hrSuccess)
- return hr;
- break;
- case PT_I8:
- if(lpProp->ulPropTag & MV_FLAG) {
- hr = HrWriteDWord(lpStream, lpProp->Value.MVli.lpli[ulMVProp].LowPart);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, lpProp->Value.MVli.lpli[ulMVProp].HighPart);
- } else {
- hr = HrWriteDWord(lpStream, lpProp->Value.li.LowPart);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, lpProp->Value.li.HighPart);
- }
- if (hr != hrSuccess)
- return hr;
- break;
- case PT_STRING8:
- if(lpProp->ulPropTag & MV_FLAG) {
- ulLen = strlen(lpProp->Value.MVszA.lppszA[ulMVProp])+1;
- hr = HrWriteDWord(lpStream, ulLen);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteData(lpStream, lpProp->Value.MVszA.lppszA[ulMVProp], ulLen);
- } else {
- ulLen = strlen(lpProp->Value.lpszA)+1;
- hr = HrWriteDWord(lpStream, 1); // unknown why this is here
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, ulLen);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteData(lpStream, lpProp->Value.lpszA, ulLen);
- }
- if (hr != hrSuccess)
- return hr;
- // Align to 4-byte boundary
- while(ulLen & 3) {
- hr = HrWriteByte(lpStream, 0);
- if (hr != hrSuccess)
- return hr;
- ++ulLen;
- }
- break;
- case PT_UNICODE:
- // Make sure we write UCS-2, since that's the format of PT_UNICODE in Win32.
- if(lpProp->ulPropTag & MV_FLAG) {
- ucs2 = converter.convert_to<std::u16string>(lpProp->Value.MVszW.lppszW[ulMVProp]);
- ulLen = ucs2.length() * sizeof(std::u16string::value_type) + sizeof(std::u16string::value_type);
- hr = HrWriteDWord(lpStream, ulLen);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteData(lpStream, (char *)ucs2.c_str(), ulLen);
- } else {
- ucs2 = converter.convert_to<std::u16string>(lpProp->Value.lpszW);
- ulLen = ucs2.length() * sizeof(std::u16string::value_type) + sizeof(std::u16string::value_type);
- hr = HrWriteDWord(lpStream, 1); // unknown why this is here
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, ulLen);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteData(lpStream, (char *)ucs2.c_str(), ulLen);
- }
- if (hr != hrSuccess)
- return hr;
- // Align to 4-byte boundary
- while(ulLen & 3) {
- hr = HrWriteByte(lpStream, 0);
- if (hr != hrSuccess)
- return hr;
- ++ulLen;
- }
- break;
- case PT_OBJECT:
- case PT_BINARY:
- if(lpProp->ulPropTag & MV_FLAG) {
- ulLen = lpProp->Value.MVbin.lpbin[ulMVProp].cb;
- hr = HrWriteDWord(lpStream, ulLen);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteData(lpStream, (char *)lpProp->Value.MVbin.lpbin[ulMVProp].lpb, ulLen);
- } else {
- ulLen = lpProp->Value.bin.cb;
- hr = HrWriteDWord(lpStream, 1); // unknown why this is here
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpStream, ulLen + (PROP_TYPE(lpProp->ulPropTag) == PT_OBJECT ? sizeof(GUID) : 0));
- if(hr != hrSuccess)
- return hr;
- if(PROP_TYPE(lpProp->ulPropTag) == PT_OBJECT)
- HrWriteData(lpStream, (char *)&IID_IStorage, sizeof(GUID));
- hr = HrWriteData(lpStream, (char *)lpProp->Value.bin.lpb, ulLen);
- }
- if (hr != hrSuccess)
- return hr;
- // Align to 4-byte boundary
- while(ulLen & 3) {
- hr = HrWriteByte(lpStream, 0);
- if (hr != hrSuccess)
- return hr;
- ++ulLen;
- }
- break;
-
- case PT_CLSID:
- if (lpProp->ulPropTag & MV_FLAG)
- hr = HrWriteData(lpStream, (char *)&lpProp->Value.MVguid.lpguid[ulMVProp], sizeof(GUID));
- else
- hr = HrWriteData(lpStream, (char *)lpProp->Value.lpguid, sizeof(GUID));
- if (hr != hrSuccess)
- return hr;
- break;
- default:
- hr = MAPI_E_INVALID_PARAMETER;
- }
- }
- return hr;
- }
- /**
- * Read from lpBuffer with size ulSize TNEF properties, and save those
- * in the proplist.
- *
- * @param[in] lpBuffer (part of) a TNEF stream which contains properties
- * @param[in] ulSize size of contents in lpBuffer
- * @param[in,out] proplist reference to an existing porplist to append properties to
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrReadPropStream(const char *lpBuffer, ULONG ulSize,
- std::list<SPropValue *> &proplist)
- {
- ULONG ulRead = 0;
- ULONG ulProps = 0;
- LPSPropValue lpProp = NULL;
- HRESULT hr = hrSuccess;
- ulProps = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- // Loop through all the properties in the data and add them to our internal list
- while(ulProps) {
- hr = HrReadSingleProp(lpBuffer, ulSize, &ulRead, &lpProp);
- if(hr != hrSuccess)
- break;
- ulSize -= ulRead;
- lpBuffer += ulRead;
- proplist.push_back(lpProp);
- --ulProps;
- if(ulRead & 3) {
- // Skip padding
- lpBuffer += 4 - (ulRead & 3);
- }
- }
- return hr;
- }
- /**
- * Read one property from a TNEF block in a buffer, and return the
- * read property and bytes read from the stream.
- *
- * @param[in] lpBuffer TNEF stream buffer
- * @param[in] ulSize size of lpBuffer
- * @param[out] lpulRead number of bytes read from lpBuffer to make lppProp
- * @param[out] lppProp returns MAPIAllocateBuffer allocated pointer if return is hrSuccess
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrReadSingleProp(const char *lpBuffer, ULONG ulSize,
- ULONG *lpulRead, LPSPropValue *lppProp)
- {
- HRESULT hr = hrSuccess;
- ULONG ulPropTag = 0;
- ULONG ulLen = 0;
- ULONG ulOrigSize = ulSize;
- ULONG ulIsNameId = 0;
- ULONG ulCount = 0;
- ULONG ulMVProp = 0;
- memory_ptr<SPropValue> lpProp;
- GUID sGuid;
- MAPINAMEID sNameID;
- LPMAPINAMEID lpNameID = &sNameID;
- memory_ptr<SPropTagArray> lpPropTags;
- std::wstring strUnicodeName;
- std::u16string ucs2;
- if(ulSize < 8)
- return MAPI_E_NOT_FOUND;
- ulPropTag = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpBuffer += sizeof(ULONG);
- ulSize -= 4;
- hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpProp);
- if(hr != hrSuccess)
- return hr;
- if(PROP_ID(ulPropTag) >= 0x8000) {
- // Named property, first read GUID, then name/id
- if (ulSize < 24)
- return MAPI_E_CORRUPT_DATA;
- memcpy(&sGuid, lpBuffer, sizeof(GUID));
- lpBuffer += sizeof(GUID);
- ulSize -= sizeof(GUID);
- ulIsNameId = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- if(ulIsNameId != 0) {
- // A string name follows
- ulLen = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- if (ulLen > ulSize)
- return MAPI_E_CORRUPT_DATA;
- // copy through u16string so we can set the boundary to the given length
- ucs2.assign(reinterpret_cast<const std::u16string::value_type *>(lpBuffer), ulLen / sizeof(std::u16string::value_type));
- strUnicodeName = convert_to<std::wstring>(ucs2);
- sNameID.ulKind = MNID_STRING;
- sNameID.Kind.lpwstrName = (WCHAR *)strUnicodeName.c_str();
- lpBuffer += ulLen;
- ulSize -= ulLen;
- // Re-align
- lpBuffer += ulLen & 3 ? 4 - (ulLen & 3) : 0;
- ulSize -= ulLen & 3 ? 4 - (ulLen & 3) : 0;
- } else {
- sNameID.ulKind = MNID_ID;
- sNameID.Kind.lID = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- }
- sNameID.lpguid = &sGuid;
- hr = m_lpMessage->GetIDsFromNames(1, &lpNameID, MAPI_CREATE, &~lpPropTags);
- if(hr != hrSuccess)
- return hr;
- // Use the mapped ID, not the original ID. The original ID is discarded
- ulPropTag = PROP_TAG(PROP_TYPE(ulPropTag), PROP_ID(lpPropTags->aulPropTag[0]));
- }
- if(ulPropTag & MV_FLAG) {
- if (ulSize < 4)
- return MAPI_E_CORRUPT_DATA;
- ulCount = *(ULONG *)lpBuffer;
- lpBuffer += 4;
- ulSize -= 4;
-
- switch(PROP_TYPE(ulPropTag)) {
- case PT_MV_I2:
- lpProp->Value.MVi.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(unsigned short), lpProp, (void **)&lpProp->Value.MVi.lpi);
- break;
- case PT_MV_LONG:
- lpProp->Value.MVl.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(ULONG), lpProp, (void **)&lpProp->Value.MVl.lpl);
- break;
- case PT_MV_R4:
- lpProp->Value.MVflt.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(float), lpProp, (void **)&lpProp->Value.MVflt.lpflt);
- break;
- case PT_MV_APPTIME:
- lpProp->Value.MVat.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(double), lpProp, (void **)&lpProp->Value.MVat.lpat);
- break;
- case PT_MV_DOUBLE:
- lpProp->Value.MVdbl.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(double), lpProp, (void **)&lpProp->Value.MVdbl.lpdbl);
- break;
- case PT_MV_CURRENCY:
- lpProp->Value.MVcur.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(CURRENCY), lpProp, (void **)&lpProp->Value.MVcur.lpcur);
- break;
- case PT_MV_SYSTIME:
- lpProp->Value.MVft.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(FILETIME), lpProp, (void **)&lpProp->Value.MVft.lpft);
- break;
- case PT_MV_I8:
- lpProp->Value.MVli.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(LARGE_INTEGER), lpProp, (void **)&lpProp->Value.MVli.lpli);
- break;
- case PT_MV_STRING8:
- lpProp->Value.MVszA.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(char *), lpProp, (void **)&lpProp->Value.MVszA.lppszA);
- break;
- case PT_MV_UNICODE:
- lpProp->Value.MVszW.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(WCHAR *), lpProp, (void **)&lpProp->Value.MVszW.lppszW);
- break;
- case PT_MV_BINARY:
- lpProp->Value.MVbin.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(SBinary), lpProp, (void **)&lpProp->Value.MVbin.lpbin);
- break;
- case PT_MV_CLSID:
- lpProp->Value.MVguid.cValues = ulCount;
- hr = MAPIAllocateMore(ulCount * sizeof(GUID), lpProp, (void **)&lpProp->Value.MVguid.lpguid);
- break;
- default:
- return MAPI_E_INVALID_PARAMETER;
- }
- } else {
- ulCount = 1;
- }
- if(hr != hrSuccess)
- return hr;
- lpProp->ulPropTag = ulPropTag;
- for (ulMVProp = 0; ulMVProp < ulCount; ++ulMVProp) {
- switch(PROP_TYPE(ulPropTag) & ~MV_FLAG) {
- case PT_I2:
- if(ulPropTag & MV_FLAG)
- lpProp->Value.MVi.lpi[ulMVProp] = *reinterpret_cast<const unsigned short *>(lpBuffer);
- else
- lpProp->Value.i = *reinterpret_cast<const unsigned short *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- break;
- case PT_LONG:
- if(ulPropTag & MV_FLAG)
- lpProp->Value.MVl.lpl[ulMVProp] = *reinterpret_cast<const ULONG *>(lpBuffer);
- else
- lpProp->Value.ul = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- break;
- case PT_BOOLEAN:
- lpProp->Value.b = *reinterpret_cast<const BOOL *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- break;
- case PT_R4:
- if(ulPropTag & MV_FLAG)
- lpProp->Value.MVflt.lpflt[ulMVProp] = *reinterpret_cast<const float *>(lpBuffer);
- else
- lpProp->Value.flt = *reinterpret_cast<const float *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- break;
- case PT_APPTIME:
- if (ulSize < 8)
- return MAPI_E_CORRUPT_DATA;
- if(ulPropTag & MV_FLAG)
- lpProp->Value.MVat.lpat[ulMVProp] = *reinterpret_cast<const double *>(lpBuffer);
- else
- lpProp->Value.at = *reinterpret_cast<const double *>(lpBuffer);
- lpBuffer += 8;
- ulSize -= 8;
- break;
- case PT_DOUBLE:
- if (ulSize < 8)
- return MAPI_E_CORRUPT_DATA;
- if(ulPropTag & MV_FLAG)
- lpProp->Value.MVdbl.lpdbl[ulMVProp] = *reinterpret_cast<const double *>(lpBuffer);
- else
- lpProp->Value.dbl = *reinterpret_cast<const double *>(lpBuffer);
- lpBuffer += 8;
- ulSize -= 8;
- break;
- case PT_CURRENCY:
- if (ulSize < 8)
- return MAPI_E_CORRUPT_DATA;
- if(ulPropTag & MV_FLAG) {
- lpProp->Value.MVcur.lpcur[ulMVProp].Lo = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpProp->Value.MVcur.lpcur[ulMVProp].Hi = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
- } else {
- lpProp->Value.cur.Lo = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpProp->Value.cur.Hi = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
- }
- lpBuffer += 8;
- ulSize -= 8;
- break;
- case PT_SYSTIME:
- if (ulSize < 8)
- return MAPI_E_CORRUPT_DATA;
- if(ulPropTag & MV_FLAG) {
- lpProp->Value.MVft.lpft[ulMVProp].dwLowDateTime = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpProp->Value.MVft.lpft[ulMVProp].dwHighDateTime = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
- } else {
- lpProp->Value.ft.dwLowDateTime = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpProp->Value.ft.dwHighDateTime = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
- }
- lpBuffer += 8;
- ulSize -= 8;
- break;
- case PT_I8:
- if (ulSize < 8)
- return MAPI_E_CORRUPT_DATA;
- if(ulPropTag & MV_FLAG) {
- lpProp->Value.MVli.lpli[ulMVProp].LowPart = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpProp->Value.MVli.lpli[ulMVProp].HighPart = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
- } else {
- lpProp->Value.li.LowPart = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpProp->Value.li.HighPart = *reinterpret_cast<const ULONG *>(lpBuffer + 4);
- }
- lpBuffer += 8;
- ulSize -= 8;
- break;
- case PT_STRING8:
- if (ulSize < 8)
- return MAPI_E_CORRUPT_DATA;
- if((PROP_TYPE(ulPropTag) & MV_FLAG) == 0) {
- lpBuffer += 4; // Skip next 4 bytes, they are always '1'
- ulSize -= 4;
- }
- ulLen = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- if (ulSize < ulLen)
- return MAPI_E_CORRUPT_DATA;
- if(ulPropTag & MV_FLAG) {
- hr = MAPIAllocateMore(ulLen+1, lpProp, (void **)&lpProp->Value.MVszA.lppszA[ulMVProp]);
- if(hr != hrSuccess)
- return hr;
- memcpy(lpProp->Value.MVszA.lppszA[ulMVProp], lpBuffer, ulLen);
- lpProp->Value.MVszA.lppszA[ulMVProp][ulLen] = 0; // should be terminated anyway but we terminte it just to be sure
- } else {
- hr = MAPIAllocateMore(ulLen+1, lpProp, (void **)&lpProp->Value.lpszA);
- if(hr != hrSuccess)
- return hr;
- memcpy(lpProp->Value.lpszA, lpBuffer, ulLen);
- lpProp->Value.lpszA[ulLen] = 0; // should be terminated anyway but we terminte it just to be sure
- }
- lpBuffer += ulLen;
- ulSize -= ulLen;
- // Re-align
- lpBuffer += ulLen & 3 ? 4 - (ulLen & 3) : 0;
- ulSize -= ulLen & 3 ? 4 - (ulLen & 3) : 0;
- break;
- case PT_UNICODE:
- // Make sure we read UCS-2, since that is the format of PT_UNICODE in Win32.
- if (ulSize < 8)
- return MAPI_E_CORRUPT_DATA;
- if((PROP_TYPE(ulPropTag) & MV_FLAG) == 0) {
- lpBuffer += 4; // Skip next 4 bytes, they are always '1'
- ulSize -= 4;
- }
- ulLen = *reinterpret_cast<const ULONG *>(lpBuffer); // Assumes 'len' in file is BYTES, not chars
- lpBuffer += 4;
- ulSize -= 4;
- if (ulSize < ulLen)
- return MAPI_E_CORRUPT_DATA;
- // copy through u16string so we can set the boundary to the given length
- ucs2.assign(reinterpret_cast<const std::u16string::value_type *>(lpBuffer), ulLen / sizeof(std::u16string::value_type));
- strUnicodeName = convert_to<std::wstring>(ucs2);
- if(ulPropTag & MV_FLAG) {
- hr = MAPIAllocateMore((strUnicodeName.length()+1) * sizeof(WCHAR), lpProp, (void **)&lpProp->Value.MVszW.lppszW[ulMVProp]);
- if(hr != hrSuccess)
- return hr;
- wcscpy(lpProp->Value.MVszW.lppszW[ulMVProp], strUnicodeName.c_str());
- } else {
- hr = MAPIAllocateMore((strUnicodeName.length()+1) * sizeof(WCHAR), lpProp, (void **)&lpProp->Value.lpszW);
- if(hr != hrSuccess)
- return hr;
- wcscpy(lpProp->Value.lpszW, strUnicodeName.c_str());
- }
- lpBuffer += ulLen;
- ulSize -= ulLen;
- // Re-align
- lpBuffer += ulLen & 3 ? 4 - (ulLen & 3) : 0;
- ulSize -= ulLen & 3 ? 4 - (ulLen & 3) : 0;
- break;
- case PT_OBJECT: // PST sends PT_OBJECT data. Treat as PT_BINARY
- case PT_BINARY:
- if (ulSize < 8)
- return MAPI_E_CORRUPT_DATA;
- if((PROP_TYPE(ulPropTag) & MV_FLAG) == 0) {
- lpBuffer += 4; // Skip next 4 bytes, it's always '1' (ULONG)
- ulSize -= 4;
- }
- ulLen = *reinterpret_cast<const ULONG *>(lpBuffer);
- lpBuffer += 4;
- ulSize -= 4;
- if (PROP_TYPE(ulPropTag) == PT_OBJECT) {
- // Can be IID_IMessage, IID_IStorage, IID_IStream (and possibly others)
- lpBuffer += 16;
- ulSize -= 16;
- ulLen -= 16;
- }
- if (ulSize < ulLen)
- return MAPI_E_CORRUPT_DATA;
- if(ulPropTag & MV_FLAG) {
- hr = MAPIAllocateMore(ulLen, lpProp, (void **)&lpProp->Value.MVbin.lpbin[ulMVProp].lpb);
- if(hr != hrSuccess)
- return hr;
- memcpy(lpProp->Value.MVbin.lpbin[ulMVProp].lpb, lpBuffer, ulLen);
- lpProp->Value.MVbin.lpbin[ulMVProp].cb = ulLen;
- } else {
- hr = MAPIAllocateMore(ulLen, lpProp, (void **)&lpProp->Value.bin.lpb);
- if(hr != hrSuccess)
- return hr;
- memcpy(lpProp->Value.bin.lpb, lpBuffer, ulLen);
- lpProp->Value.bin.cb = ulLen;
- }
- lpBuffer += ulLen;
- ulSize -= ulLen;
- // Re-align
- lpBuffer += ulLen & 3 ? 4 - (ulLen & 3) : 0;
- ulSize -= ulLen & 3 ? 4 - (ulLen & 3) : 0;
- break;
- case PT_CLSID:
- if (ulSize < sizeof(GUID))
- return MAPI_E_CORRUPT_DATA;
- if(ulPropTag & MV_FLAG) {
- memcpy(&lpProp->Value.MVguid.lpguid[ulMVProp], lpBuffer, sizeof(GUID));
- } else {
- hr = MAPIAllocateMore(sizeof(GUID), lpProp, (LPVOID*)&lpProp->Value.lpguid);
- if (hr != hrSuccess)
- return hr;
- memcpy(lpProp->Value.lpguid, lpBuffer, sizeof(GUID));
- }
- lpBuffer += sizeof(GUID);
- ulSize -= sizeof(GUID);
- break;
- default:
- hr = MAPI_E_INVALID_PARAMETER;
- break;
- }
- }
- *lpulRead = ulOrigSize - ulSize;
- *lppProp = lpProp.release();
- return hr;
- }
- /**
- * Add specified properties to the TNEF object list to save with
- * Finish. This makes a lazy copy of the lpProps, so make sure you keep
- * them in memory until you call Finish().
- *
- * @param[in] cValues Number of properties in lpProps.
- * @param[in] lpProps Array of properties to add to the TNEF object
- * @retval hrSuccess
- */
- HRESULT ECTNEF::SetProps(ULONG cValues, LPSPropValue lpProps)
- {
- unsigned int i = 0;
- for (i = 0; i < cValues; ++i)
- lstProps.push_back(&lpProps[i]);
- return hrSuccess;
- }
- /**
- * Add another component to the TNEF stream. This currently only works
- * for attachments, and you have to pass the PR_ATTACH_NUM in
- * 'ulComponentID'. We then serialize all properties passed in
- * lpPropList into the TNEF stream. Currently we do NOT support
- * ATTACH_EMBEDDED_MSG type attachments - this function is currently
- * only really useful for ATTACH_OLE attachments.
- *
- * @param[in] ulFlags Must be TNEF_COMPONENT_ATTACHMENT, others currently not supported.
- * @param[in] ulComponentID PR_ATTACH_NUM value passed to OpenAttachment()
- * @param[in] lpPropList List of proptags to put in the TNEF stream of this attachment
- * @return MAPI error code
- */
- HRESULT ECTNEF::FinishComponent(ULONG ulFlags, ULONG ulComponentID,
- const SPropTagArray *lpPropList)
- {
- HRESULT hr = hrSuccess;
- object_ptr<IAttach> lpAttach;
- memory_ptr<SPropValue> lpProps, lpAttachProps, lpsNewProp;
- object_ptr<IStream> lpStream;
- ULONG cValues = 0;
- AttachRendData sData;
- static constexpr const SizedSPropTagArray(2, sptaTags) =
- {2, {PR_ATTACH_METHOD, PR_RENDERING_POSITION}};
- struct tnefattachment sTnefAttach;
-
- if (ulFlags != TNEF_COMPONENT_ATTACHMENT)
- return MAPI_E_NO_SUPPORT;
- if (this->ulFlags != TNEF_ENCODE)
- return MAPI_E_INVALID_PARAMETER;
- hr = m_lpMessage->OpenAttach(ulComponentID, &IID_IAttachment, 0, &~lpAttach);
- if(hr != hrSuccess)
- return hr;
-
- // Get some properties we always need
- hr = lpAttach->GetProps(sptaTags, 0, &cValues, &~lpAttachProps);
- if(FAILED(hr))
- return hr;
-
- // ignore warnings
- hr = hrSuccess;
-
- memset(&sData, 0, sizeof(sData));
- sData.usType = lpAttachProps[0].ulPropTag == PR_ATTACH_METHOD && lpAttachProps[0].Value.ul == ATTACH_OLE ? AttachTypeOle : AttachTypeFile;
- sData.ulPosition = lpAttachProps[1].ulPropTag == PR_RENDERING_POSITION ? lpAttachProps[1].Value.ul : 0;
-
- // Get user-passed properties
- hr = lpAttach->GetProps(lpPropList, 0, &cValues, &~lpProps);
- if(FAILED(hr))
- return hr;
-
- for (unsigned int i = 0; i < cValues; ++i) {
- // Other properties
- if(PROP_TYPE(lpProps[i].ulPropTag) == PT_ERROR)
- continue;
- hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpsNewProp);
- if(hr != hrSuccess)
- return hr;
-
- if(PROP_TYPE(lpProps[i].ulPropTag) == PT_OBJECT) {
- // PT_OBJECT requested, open object as stream and read the data
- hr = lpAttach->OpenProperty(lpProps[i].ulPropTag, &IID_IStream, 0, 0, &~lpStream);
- if(hr != hrSuccess)
- return hr;
-
- // We save the actual data same way as PT_BINARY
- hr = HrReadStream(lpStream, lpsNewProp, &lpsNewProp->Value.bin.lpb, &lpsNewProp->Value.bin.cb);
- if(hr != hrSuccess)
- return hr;
-
- lpsNewProp->ulPropTag = lpProps[i].ulPropTag;
- } else {
- hr = Util::HrCopyProperty(lpsNewProp, &lpProps[i], lpsNewProp);
- if(hr != hrSuccess)
- return hr;
- }
- sTnefAttach.lstProps.push_back(lpsNewProp.release());
- }
- sTnefAttach.rdata = sData;
- sTnefAttach.data = NULL;
- sTnefAttach.size = 0;
- lstAttachments.push_back(new tnefattachment(sTnefAttach));
- return hrSuccess;
- }
- /**
- * Finalize the TNEF object. If the constructors ulFlags was
- * TNEF_DECODE, the properties will be saved to the given message. If
- * ulFlags was TNEF_ENCODE, the lpStream will be written the TNEF
- * data.
- *
- * @return MAPI error code
- */
- HRESULT ECTNEF::Finish()
- {
- HRESULT hr = hrSuccess;
- STATSTG sStat;
- ULONG ulChecksum;
- LARGE_INTEGER zero = {{0,0}};
- ULARGE_INTEGER uzero = {{0,0}};
- // attachment vars
- ULONG ulAttachNum;
- object_ptr<IStream> lpAttStream;
- object_ptr<IMessage> lpAttMessage;
- SPropValue sProp;
- if(ulFlags == TNEF_DECODE) {
- // Write properties to message
- for (const auto p : lstProps) {
- if (PROP_ID(p->ulPropTag) == PROP_ID(PR_MESSAGE_CLASS) ||
- !FPropExists(m_lpMessage, p->ulPropTag) ||
- PROP_ID(p->ulPropTag) == PROP_ID(PR_RTF_COMPRESSED) ||
- PROP_ID(p->ulPropTag) == PROP_ID(PR_HTML) ||
- PROP_ID(p->ulPropTag) == PROP_ID(PR_INTERNET_CPID))
- m_lpMessage->SetProps(1, p, NULL);
- // else, Property already exists, do *not* overwrite it
- }
- // Add all found attachments to message
- for (const auto att : lstAttachments) {
- object_ptr<IAttach> lpAttach;
- bool has_obj = false;
- hr = m_lpMessage->CreateAttach(nullptr, 0, &ulAttachNum, &~lpAttach);
- if (hr != hrSuccess)
- return hr;
-
- sProp.ulPropTag = PR_ATTACH_METHOD;
- if (att->rdata.usType == AttachTypeOle)
- sProp.Value.ul = ATTACH_OLE;
- else
- sProp.Value.ul = ATTACH_BY_VALUE;
- lpAttach->SetProps(1, &sProp, NULL);
- sProp.ulPropTag = PR_RENDERING_POSITION;
- sProp.Value.ul = att->rdata.ulPosition;
- lpAttach->SetProps(1, &sProp, NULL);
-
- for (const auto p : att->lstProps) {
- // must not set PR_ATTACH_NUM by ourselves
- if (PROP_ID(p->ulPropTag) == PROP_ID(PR_ATTACH_NUM))
- continue;
- if (p->ulPropTag != PR_ATTACH_DATA_OBJ) {
- hr = lpAttach->SetProps(1, p, NULL);
- if (hr != hrSuccess)
- return hr;
- } else {
- // message in PT_OBJECT, was saved in Value.bin
- if (att->rdata.usType == AttachTypeOle) {
- object_ptr<IStream> lpSubStream;
- hr = lpAttach->OpenProperty(p->ulPropTag, &IID_IStream, 0, MAPI_CREATE | MAPI_MODIFY, &~lpSubStream);
- if(hr != hrSuccess)
- return hr;
- hr = lpSubStream->Write(p->Value.bin.lpb, p->Value.bin.cb, NULL);
- if(hr != hrSuccess)
- return hr;
- hr = lpSubStream->Commit(0);
- if(hr != hrSuccess)
- return hr;
- has_obj = true;
- } else {
- object_ptr<IStream> lpSubStream;
- hr = CreateStreamOnHGlobal(nullptr, TRUE, &~lpSubStream);
- if (hr != hrSuccess)
- return hr;
- hr = lpSubStream->Write(p->Value.bin.lpb, p->Value.bin.cb, NULL);
- if (hr != hrSuccess)
- return hr;
- hr = lpSubStream->Seek(zero, STREAM_SEEK_SET, NULL);
- if(hr != hrSuccess)
- return hr;
- hr = lpAttach->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY, &~lpAttMessage);
- if(hr != hrSuccess)
- return hr;
- ECTNEF SubTNEF(TNEF_DECODE, lpAttMessage, lpSubStream);
- hr = SubTNEF.ExtractProps(TNEF_PROP_EXCLUDE, NULL);
- if (hr != hrSuccess)
- return hr;
- hr = SubTNEF.Finish();
- if (hr != hrSuccess)
- return hr;
- hr = lpAttMessage->SaveChanges(0);
- if (hr != hrSuccess)
- return hr;
- has_obj = true;
- }
- }
- }
- if (!has_obj && att->data != NULL) {
- object_ptr<IStream> lpAttStream;
- hr = lpAttach->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~lpAttStream);
- if (hr != hrSuccess)
- return hr;
- hr = lpAttStream->Write(att->data, att->size,NULL);
- if (hr != hrSuccess)
- return hr;
- hr = lpAttStream->Commit(0);
- if (hr != hrSuccess)
- return hr;
- }
- hr = lpAttach->SaveChanges(0);
- if (hr != hrSuccess)
- return hr;
- }
- } else if(ulFlags == TNEF_ENCODE) {
- // Write properties to stream
- hr = HrWriteDWord(m_lpStream, TNEF_SIGNATURE);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteWord(m_lpStream, 0); // Write Key
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteByte(m_lpStream, 1); // Write component (always 1 ?)
- if(hr != hrSuccess)
- return hr;
- object_ptr<IStream> lpPropStream;
- hr = CreateStreamOnHGlobal(nullptr, TRUE, &~lpPropStream);
- if(hr != hrSuccess)
- return hr;
- hr = HrWritePropStream(lpPropStream, lstProps);
- if(hr != hrSuccess)
- return hr;
- hr = lpPropStream->Stat(&sStat, STATFLAG_NONAME);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(m_lpStream, 0x00069003);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(m_lpStream, sStat.cbSize.LowPart); // Write size
- if(hr != hrSuccess)
- return hr;
- hr = lpPropStream->Seek(zero, STREAM_SEEK_SET, NULL);
- if(hr != hrSuccess)
- return hr;
- hr = lpPropStream->CopyTo(m_lpStream, sStat.cbSize, NULL, NULL); // Write data
- if(hr != hrSuccess)
- return hr;
- hr = lpPropStream->Seek(zero, STREAM_SEEK_SET, NULL);
- if(hr != hrSuccess)
- return hr;
- hr = HrGetChecksum(lpPropStream, &ulChecksum);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteWord(m_lpStream, (unsigned short)ulChecksum); // Write checksum
- if(hr != hrSuccess)
- return hr;
-
- // Write attachments
- for (const auto att : lstAttachments) {
- /* Write attachment start block */
- hr = HrWriteBlock(m_lpStream, reinterpret_cast<char *>(&att->rdata), sizeof(AttachRendData), 0x00069002, 2);
- if(hr != hrSuccess)
- return hr;
-
- // Write attachment data block if available
- if (att->data != NULL) {
- hr = HrWriteBlock(m_lpStream, reinterpret_cast<char *>(att->data), att->size, 0x0006800f, 2);
- if(hr != hrSuccess)
- return hr;
- }
-
- // Write property block
- hr = lpPropStream->SetSize(uzero);
- if(hr != hrSuccess)
- return hr;
- hr = HrWritePropStream(lpPropStream, att->lstProps);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteBlock(m_lpStream, lpPropStream, 0x00069005, 2);
- if(hr != hrSuccess)
- return hr;
- // Note that we don't write any other blocks like PR_ATTACH_FILENAME since this information is also in the property block
-
- }
- }
- return hrSuccess;
- }
- /**
- * Read one DWORD (32-bit unsigned integer) from input stream
- *
- * @param[in] lpStream input stream to read one ULONG from, stream automatically moves current cursor.
- * @param[out] ulData ULONG value from lpStream
- * @retval MAPI_E_NOT_FOUND if stream was too short, other MAPI error code
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrReadDWord(IStream *lpStream, ULONG *ulData)
- {
- HRESULT hr;
- ULONG ulRead = 0;
- hr = lpStream->Read(ulData, sizeof(unsigned int), &ulRead);
- if(hr != hrSuccess)
- return hr;
- if (ulRead != sizeof(unsigned int))
- return MAPI_E_NOT_FOUND;
- return hrSuccess;
- }
- /**
- * Read one WORD (16-bit unsigned integer) from input stream
- *
- * @param[in] lpStream input stream to read one unsigned short from, stream automatically moves current cursor.
- * @param[out] ulData unsigned short value from lpStream
- * @retval MAPI_E_NOT_FOUND if stream was too short, other MAPI error code
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrReadWord(IStream *lpStream, unsigned short *ulData)
- {
- HRESULT hr;
- ULONG ulRead = 0;
- hr = lpStream->Read(ulData, sizeof(unsigned short), &ulRead);
- if(hr != hrSuccess)
- return hr;
- if (ulRead != sizeof(unsigned short))
- return MAPI_E_NOT_FOUND;
- return hrSuccess;
- }
- /**
- * Read one BYTE (CHAR_BIT-bits unsigned char) from input stream
- *
- * @param[in] lpStream input stream to read one unsigned char from, stream automatically moves current cursor.
- * @param[out] ulData unsigned char value from lpStream
- * @retval MAPI_E_NOT_FOUND if stream was too short, other MAPI error code
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrReadByte(IStream *lpStream, unsigned char *ulData)
- {
- HRESULT hr;
- ULONG ulRead = 0;
- hr = lpStream->Read(ulData, 1, &ulRead);
- if(hr != hrSuccess)
- return hr;
- if (ulRead != 1)
- return MAPI_E_NOT_FOUND;
- return hrSuccess;
- }
- /**
- * Read a block of data from the stream, with given length. Will be
- * processes in blocks of 4096 bytes.
- *
- * @param[in] lpStream input stream to read one unsigned char from, stream automatically moves current cursor.
- * @param[out] lpData pre-allocated buffer of size given in ulLen
- * @param[in] ulLen Length to read from stream, and thus size of lpData
- * @retval MAPI_E_NOT_FOUND if stream was too short, other MAPI error code
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrReadData(IStream *lpStream, char *lpData, ULONG ulLen)
- {
- HRESULT hr;
- ULONG ulRead = 0;
- ULONG ulToRead = 0;
- while(ulLen) {
- ulToRead = ulLen > 4096 ? 4096 : ulLen;
- hr = lpStream->Read(lpData, ulToRead, &ulRead);
- if(hr != hrSuccess)
- return hr;
- if (ulRead != ulToRead)
- return MAPI_E_NOT_FOUND;
- ulLen -= ulRead;
- lpData += ulRead;
- }
- return hrSuccess;
- }
- /**
- * Write one DWORD (32-bit integer) to output stream
- *
- * @param[in,out] lpStream stream to write one ULONG to, stream automatically moves current cursor.
- * @param[in] ulData ULONG value to write in lpStream
- * @retval MAPI_E_NOT_FOUND if stream was not written the same bytes, other MAPI error code
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrWriteDWord(IStream *lpStream, ULONG ulData)
- {
- HRESULT hr;
- ULONG ulWritten = 0;
- hr = lpStream->Write(&ulData, sizeof(unsigned int), &ulWritten);
- if(hr != hrSuccess)
- return hr;
- if (ulWritten != sizeof(unsigned int))
- return MAPI_E_NOT_FOUND;
- return hrSuccess;
- }
- /**
- * Write one WORD (16-bit unsigned integer) to output stream
- *
- * @param[in,out] lpStream stream to write one unsigned short to, stream automatically moves current cursor.
- * @param[in] ulData unsigned short value to write in lpStream
- * @retval MAPI_E_NOT_FOUND if stream was not written the same bytes, other MAPI error code
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrWriteWord(IStream *lpStream, unsigned short ulData)
- {
- HRESULT hr;
- ULONG ulWritten = 0;
- hr = lpStream->Write(&ulData, sizeof(unsigned short), &ulWritten);
- if(hr != hrSuccess)
- return hr;
- if (ulWritten != sizeof(unsigned short))
- return MAPI_E_NOT_FOUND;
- return hrSuccess;
- }
- /**
- * Write one BYTE (8-bit unsigned integer) to output stream
- *
- * @param[in,out] lpStream stream to write one unsigned char to, stream automatically moves current cursor.
- * @param[in] ulData unsigned char value to write in lpStream
- * @retval MAPI_E_NOT_FOUND if stream was not written the same bytes, other MAPI error code
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrWriteByte(IStream *lpStream, unsigned char ulData)
- {
- HRESULT hr;
- ULONG ulWritten = 0;
- hr = lpStream->Write(&ulData, sizeof(unsigned char), &ulWritten);
- if(hr != hrSuccess)
- return hr;
- if (ulWritten != sizeof(unsigned char))
- return MAPI_E_NOT_FOUND;
- return hrSuccess;
- }
- /**
- * Write a block of data of given size to output stream
- *
- * @param[in,out] lpStream stream to write one unsigned char to, stream automatically moves current cursor.
- * @param[in] ulData unsigned char value to write in lpStream
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrWriteData(IStream *lpStream, const char *data, ULONG ulLen)
- {
- HRESULT hr;
- ULONG ulWritten = 0;
- while(ulLen > 0) {
- hr = lpStream->Write(data, ulLen > 4096 ? 4096 : ulLen, &ulWritten);
- if(hr != hrSuccess)
- return hr;
- ulLen -= ulWritten;
- data += ulWritten;
- }
- return hrSuccess;
- }
- /**
- * TNEF uses the rather stupid checksum of adding all the bytes in the stream.
- * Was TNEF coded by an intern or something ??
- *
- * @param[in] lpStream Input TNEF stream, this object will be unmodified
- * @param[out] lpulChecksum "Checksum" of the TNEF data
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrGetChecksum(IStream *lpStream, ULONG *lpulChecksum)
- {
- HRESULT hr = hrSuccess;
- ULONG ulChecksum = 0;
- object_ptr<IStream> lpClone;
- LARGE_INTEGER zero = {{0,0}};
- ULONG ulRead = 0;
- unsigned char buffer[4096];
- unsigned int i = 0;
- hr = lpStream->Clone(&~lpClone);
- if(hr != hrSuccess)
- return hr;
- hr = lpClone->Seek(zero, STREAM_SEEK_SET, NULL);
- if(hr != hrSuccess)
- return hr;
- while(TRUE) {
- hr = lpClone->Read(buffer, 4096, &ulRead);
- if(hr != hrSuccess)
- return hr;
- if(ulRead == 0)
- break;
- for (i = 0; i < ulRead; ++i)
- ulChecksum += buffer[i];
- }
- *lpulChecksum = ulChecksum;
- return hrSuccess;
- }
- /**
- * Create a TNEF checksum over a normal char buffer.
- *
- * @param[in] lpData Buffer containing TNEF data
- * @param[in] ulLen Length of lpData
- * @return TNEF checksum value
- */
- ULONG ECTNEF::GetChecksum(const char *lpData, unsigned int ulLen) const
- {
- ULONG ulChecksum = 0;
- for (unsigned int i = 0; i < ulLen; ++i)
- ulChecksum += lpData[i];
- return ulChecksum;
- }
- /**
- * Copy stream data to another stream with given TNEF block id and level number.
- *
- * @param[in,out] lpDestStream Stream to write data to
- * @param[in] lpSourceStream Stream to read data from
- * @param[in] ulBlockID TNEF block id number
- * @param[in] ulLevel TNEF level number
- *
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrWriteBlock(IStream *lpDestStream, IStream *lpSourceStream, ULONG ulBlockID, ULONG ulLevel)
- {
- HRESULT hr;
- ULONG ulChecksum = 0;
- LARGE_INTEGER zero = {{0,0}};
- STATSTG sStat;
- hr = HrWriteByte(lpDestStream, ulLevel);
- if(hr != hrSuccess)
- return hr;
- hr = HrGetChecksum(lpSourceStream, &ulChecksum);
- if(hr != hrSuccess)
- return hr;
- hr = lpSourceStream->Seek(zero, STREAM_SEEK_SET, NULL);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpDestStream, ulBlockID);
- if(hr != hrSuccess)
- return hr;
- hr = lpSourceStream->Stat(&sStat, STATFLAG_NONAME);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteDWord(lpDestStream, sStat.cbSize.QuadPart);
- if(hr != hrSuccess)
- return hr;
- hr = lpSourceStream->CopyTo(lpDestStream, sStat.cbSize, NULL, NULL);
- if(hr != hrSuccess)
- return hr;
- hr = HrWriteWord(lpDestStream, ulChecksum);
- if(hr != hrSuccess)
- return hr;
- return hrSuccess;
- }
- /**
- * Write a buffer to a stream with given TNEF block id and level number.
- *
- * @param[in,out] lpDestStream Stream to write data block in
- * @param[in] lpData Data block to write to stream
- * @param[in] ulLen Lenght of lpData
- * @param[in] ulBlockID TNEF Block ID number
- * @param[in] ulLevel TNEF Level number
- *
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrWriteBlock(IStream *lpDestStream, const char *lpData,
- unsigned int ulLen, ULONG ulBlockID, ULONG ulLevel)
- {
- HRESULT hr = hrSuccess;
- object_ptr<IStream> lpStream;
-
- hr = CreateStreamOnHGlobal(nullptr, TRUE, &~lpStream);
- if (hr != hrSuccess)
- return hr;
- hr = lpStream->Write(lpData, ulLen, NULL);
- if (hr != hrSuccess)
- return hr;
- return HrWriteBlock(lpDestStream, lpStream, ulBlockID, ulLevel);
- }
- /**
- * Read a complete stream into a buffer. (Don't we have this function somewhere in common/ ?)
- *
- * @param[in] lpStream stream to read into buffer and return as BYTE array, cursor will be at the end on return
- * @param[in] lpBase pointer to use with MAPIAllocateMore, cannot be NULL
- * @param[out] lppData New allocated (more) buffer with contents of stream
- * @param[out] lpulSize size of *lppData buffer
- *
- * @return MAPI error code
- */
- HRESULT ECTNEF::HrReadStream(IStream *lpStream, void *lpBase, BYTE **lppData, ULONG *lpulSize)
- {
- HRESULT hr;
- STATSTG sStat;
- BYTE *lpBuffer = NULL;
- BYTE *lpWrite = NULL;
- ULONG ulSize = 0;
- ULONG ulRead = 0;
- if (lpStream == NULL || lpBase == NULL || lppData == NULL || lpulSize == NULL)
- return MAPI_E_INVALID_PARAMETER;
- hr = lpStream->Stat(&sStat, STATFLAG_NONAME);
- if(hr != hrSuccess)
- return hr;
- hr = MAPIAllocateMore(sStat.cbSize.QuadPart, lpBase, (void **)&lpBuffer);
- if(hr != hrSuccess)
- return hr;
- lpWrite = lpBuffer;
- while(sStat.cbSize.QuadPart > 0) {
- hr = lpStream->Read(lpWrite, sStat.cbSize.QuadPart, &ulRead);
- if(hr != hrSuccess)
- return hr;
- lpWrite += ulRead;
- ulSize += ulRead;
- sStat.cbSize.QuadPart -= ulRead;
- }
-
- *lppData = lpBuffer;
- *lpulSize = ulSize;
- return hrSuccess;
- }
- } /* namespace */
- /** @} */
|