ECMessage.cpp 91 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <new>
  18. #include <kopano/platform.h>
  19. #include <kopano/lockhelper.hpp>
  20. #include <kopano/mapi_ptr.h>
  21. #include <kopano/memory.hpp>
  22. #include <kopano/ECInterfaceDefs.h>
  23. #include <mapidefs.h>
  24. #include <mapiutil.h>
  25. #include <mapitags.h>
  26. #include <kopano/mapiext.h>
  27. #include "ECMessage.h"
  28. #include "ECAttach.h"
  29. #include <kopano/ECMemTable.h>
  30. #include <kopano/codepage.h>
  31. #include "rtfutil.h"
  32. #include <kopano/Util.h>
  33. #include "Mem.h"
  34. #include <kopano/mapi_ptr.h>
  35. #include <kopano/ECGuid.h>
  36. #include <edkguid.h>
  37. #include <kopano/ECDebug.h>
  38. #include "WSUtil.h"
  39. #include "ClientUtil.h"
  40. #include "ECMemStream.h"
  41. #include <kopano/charset/convert.h>
  42. #include <librosie.h>
  43. #include "config.h"
  44. using namespace std;
  45. using namespace KCHL;
  46. #define MAX_TABLE_PROPSIZE 8192
  47. static constexpr const SizedSPropTagArray(15, sPropRecipColumns) =
  48. {15, {PR_7BIT_DISPLAY_NAME_W, PR_EMAIL_ADDRESS_W, PR_INSTANCE_KEY,
  49. PR_RECORD_KEY, PR_SEARCH_KEY, PR_SEND_RICH_INFO, PR_DISPLAY_NAME_W,
  50. PR_RECIPIENT_TYPE, PR_ROWID, PR_DISPLAY_TYPE, PR_ENTRYID,
  51. PR_SPOOLER_STATUS, PR_OBJECT_TYPE, PR_ADDRTYPE_W, PR_RESPONSIBILITY}};
  52. static constexpr const SizedSPropTagArray(8, sPropAttachColumns) =
  53. {8, {PR_ATTACH_NUM, PR_INSTANCE_KEY, PR_RECORD_KEY,
  54. PR_RENDERING_POSITION, PR_ATTACH_FILENAME_W, PR_ATTACH_METHOD,
  55. PR_DISPLAY_NAME_W, PR_ATTACH_LONG_FILENAME_W}};
  56. HRESULT ECMessageFactory::Create(ECMsgStore *lpMsgStore, BOOL fNew, BOOL fModify, ULONG ulFlags, BOOL bEmbedded, ECMAPIProp *lpRoot, ECMessage **lpMessage) const
  57. {
  58. return ECMessage::Create(lpMsgStore, fNew, fModify, ulFlags, bEmbedded, lpRoot, lpMessage);
  59. }
  60. ECMessage::ECMessage(ECMsgStore *lpMsgStore, BOOL fNew, BOOL fModify,
  61. ULONG ulFlags, BOOL bEmbedded, ECMAPIProp *lpRoot) :
  62. ECMAPIProp(lpMsgStore, MAPI_MESSAGE, fModify, lpRoot, "IMessage"),
  63. m_bEmbedded(bEmbedded)
  64. {
  65. this->ulObjFlags = ulFlags & MAPI_ASSOCIATED;
  66. this->fNew = fNew;
  67. this->m_bEmbedded = bEmbedded;
  68. // proptag, getprop, setprops, class, bRemovable, bHidden
  69. this->HrAddPropHandlers(PR_RTF_IN_SYNC, GetPropHandler ,DefaultSetPropIgnore, (void*) this, TRUE, FALSE);
  70. this->HrAddPropHandlers(PR_HASATTACH, GetPropHandler ,DefaultSetPropComputed, (void*) this, FALSE, FALSE);
  71. this->HrAddPropHandlers(PR_NORMALIZED_SUBJECT, GetPropHandler ,DefaultSetPropIgnore, (void*) this, FALSE, FALSE);
  72. this->HrAddPropHandlers(PR_PARENT_ENTRYID, GetPropHandler ,DefaultSetPropComputed, (void*) this, FALSE, FALSE);
  73. this->HrAddPropHandlers(PR_MESSAGE_SIZE, GetPropHandler ,SetPropHandler, (void*) this, FALSE, FALSE);
  74. this->HrAddPropHandlers(PR_DISPLAY_TO, GetPropHandler ,DefaultSetPropComputed, (void*) this, FALSE, FALSE);
  75. this->HrAddPropHandlers(PR_DISPLAY_CC, GetPropHandler ,DefaultSetPropComputed, (void*) this, FALSE, FALSE);
  76. this->HrAddPropHandlers(PR_DISPLAY_BCC, GetPropHandler ,DefaultSetPropComputed, (void*) this, FALSE, FALSE);
  77. this->HrAddPropHandlers(PR_ACCESS, GetPropHandler ,DefaultSetPropComputed, (void*) this, FALSE, FALSE);
  78. this->HrAddPropHandlers(PR_MESSAGE_ATTACHMENTS, GetPropHandler ,DefaultSetPropIgnore, (void*) this, FALSE, FALSE);
  79. this->HrAddPropHandlers(PR_MESSAGE_RECIPIENTS, GetPropHandler ,DefaultSetPropIgnore, (void*) this, FALSE, FALSE);
  80. // Handlers for the various body types
  81. this->HrAddPropHandlers(PR_BODY, GetPropHandler ,DefaultSetPropSetReal, (void*) this, TRUE, FALSE);
  82. this->HrAddPropHandlers(PR_RTF_COMPRESSED, GetPropHandler ,DefaultSetPropSetReal, (void*) this, FALSE, FALSE);
  83. // Workaround for support html in outlook 2000/xp need SetPropHandler
  84. this->HrAddPropHandlers(PR_HTML, GetPropHandler ,SetPropHandler, (void*) this, FALSE, FALSE);
  85. this->HrAddPropHandlers(PR_EC_BODY_FILTERED, GetPropHandler, SetPropHandler, static_cast<void *>(this), false, false);
  86. // The property 0x10970003 is set by outlook when browsing in the 'unread mail' searchfolder. It is used to make sure
  87. // that a message that you just read is not removed directly from view. It is set for each message which should be in the view
  88. // even though it is 'read', and is removed when you leave the folder. When you try to export this property to a PST, you get
  89. // an access denied error. We therefore hide this property, ie you can GetProps/SetProps it and use it in a restriction, but
  90. // GetPropList will never list it (same as in a PST).
  91. this->HrAddPropHandlers(0x10970003, DefaultGetPropGetReal,DefaultSetPropSetReal, (void*) this, TRUE, TRUE);
  92. // Don't show the PR_EC_IMAP_ID, and mark it as computed and deletable. This makes sure that CopyTo() will not copy it to
  93. // the other message.
  94. this->HrAddPropHandlers(PR_EC_IMAP_ID, DefaultGetPropGetReal,DefaultSetPropComputed, (void*) this, TRUE, TRUE);
  95. // Make sure the MSGFLAG_HASATTACH flag gets added when needed.
  96. this->HrAddPropHandlers(PR_MESSAGE_FLAGS, GetPropHandler ,SetPropHandler, (void*) this, FALSE, FALSE);
  97. // Make sure PR_SOURCE_KEY is available
  98. this->HrAddPropHandlers(PR_SOURCE_KEY, GetPropHandler ,SetPropHandler, (void*) this, TRUE, FALSE);
  99. // IMAP complete email, removable and hidden. setprop ignore? use interface for single-instancing
  100. this->HrAddPropHandlers(PR_EC_IMAP_EMAIL, DefaultGetPropGetReal ,DefaultSetPropSetReal, (void*) this, TRUE, TRUE);
  101. this->HrAddPropHandlers(PR_EC_IMAP_EMAIL_SIZE, DefaultGetPropGetReal ,DefaultSetPropSetReal, (void*) this, TRUE, TRUE);
  102. this->HrAddPropHandlers(CHANGE_PROP_TYPE(PR_EC_IMAP_BODY, PT_UNICODE), DefaultGetPropGetReal ,DefaultSetPropSetReal, (void*) this, TRUE, TRUE);
  103. this->HrAddPropHandlers(CHANGE_PROP_TYPE(PR_EC_IMAP_BODYSTRUCTURE, PT_UNICODE), DefaultGetPropGetReal ,DefaultSetPropSetReal, (void*) this, TRUE, TRUE);
  104. this->HrAddPropHandlers(PR_ASSOCIATED, GetPropHandler, DefaultSetPropComputed, (void *)this, TRUE, TRUE);
  105. }
  106. ECMessage::~ECMessage()
  107. {
  108. MAPIFreeBuffer(m_lpParentID);
  109. if(lpRecips)
  110. lpRecips->Release();
  111. if(lpAttachments)
  112. lpAttachments->Release();
  113. }
  114. HRESULT ECMessage::Create(ECMsgStore *lpMsgStore, BOOL fNew, BOOL fModify, ULONG ulFlags, BOOL bEmbedded, ECMAPIProp *lpRoot, ECMessage **lppMessage)
  115. {
  116. return alloc_wrap<ECMessage>(lpMsgStore, fNew, fModify, ulFlags,
  117. bEmbedded, lpRoot).as(IID_ECMessage, lppMessage);
  118. }
  119. HRESULT ECMessage::QueryInterface(REFIID refiid, void **lppInterface)
  120. {
  121. REGISTER_INTERFACE2(ECMessage, this);
  122. REGISTER_INTERFACE2(ECMAPIProp, this);
  123. REGISTER_INTERFACE2(ECUnknown, this);
  124. REGISTER_INTERFACE2(IMessage, &this->m_xMessage);
  125. REGISTER_INTERFACE2(IMAPIProp, &this->m_xMessage);
  126. REGISTER_INTERFACE2(IUnknown, &this->m_xMessage);
  127. REGISTER_INTERFACE3(ISelectUnicode, IUnknown, &this->m_xUnknown);
  128. REGISTER_INTERFACE2(IECSingleInstance, &this->m_xECSingleInstance);
  129. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  130. }
  131. HRESULT ECMessage::GetProps(const SPropTagArray *lpPropTagArray, ULONG ulFlags,
  132. ULONG *lpcValues, SPropValue **lppPropArray)
  133. {
  134. HRESULT hr = hrSuccess;
  135. ULONG cValues = 0;
  136. SPropArrayPtr ptrPropArray;
  137. LONG lBodyIdx = 0;
  138. LONG lRtfIdx = 0;
  139. LONG lHtmlIdx = 0;
  140. if (lpPropTagArray) {
  141. lBodyIdx = Util::FindPropInArray(lpPropTagArray, CHANGE_PROP_TYPE(PR_BODY_W, PT_UNSPECIFIED));
  142. lRtfIdx = Util::FindPropInArray(lpPropTagArray, CHANGE_PROP_TYPE(PR_RTF_COMPRESSED, PT_UNSPECIFIED));
  143. lHtmlIdx = Util::FindPropInArray(lpPropTagArray, CHANGE_PROP_TYPE(PR_HTML, PT_UNSPECIFIED));
  144. }
  145. if (lstProps == NULL && (!lpPropTagArray || lBodyIdx >= 0 || lRtfIdx >=0 || lHtmlIdx >= 0)) {
  146. // Get the properties from the server so we can determine the body type.
  147. m_ulBodyType = bodyTypeUnknown; // Make sure no bodies are generated.
  148. hr = HrLoadProps(); // HrLoadProps will (re)determine the best body type.
  149. if (hr != hrSuccess)
  150. return hr;
  151. }
  152. if (m_ulBodyType != bodyTypeUnknown) {
  153. const ULONG ulBestMatchTable[4][3] = {
  154. /* unknown */ { PR_BODY_W, PR_RTF_COMPRESSED, PR_HTML },
  155. /* plain */ { PR_BODY_W, PR_RTF_COMPRESSED, PR_HTML },
  156. /* rtf */ { PR_RTF_COMPRESSED, PR_HTML, PR_BODY_W },
  157. /* html */ { PR_HTML, PR_RTF_COMPRESSED, PR_BODY_W }};
  158. SPropTagArrayPtr ptrPropTagArray;
  159. ULONG ulBestMatch = 0;
  160. if (lpPropTagArray) {
  161. // Use a temporary SPropTagArray so we can safely modify it.
  162. hr = Util::HrCopyPropTagArray(lpPropTagArray, &~ptrPropTagArray);
  163. if (hr != hrSuccess)
  164. return hr;
  165. } else {
  166. // Get the proplist, so we can filter it.
  167. hr = GetPropList(ulFlags, &~ptrPropTagArray);
  168. if (hr != hrSuccess)
  169. return hr;
  170. lBodyIdx = Util::FindPropInArray(ptrPropTagArray, CHANGE_PROP_TYPE(PR_BODY_W, PT_UNSPECIFIED));
  171. lRtfIdx = Util::FindPropInArray(ptrPropTagArray, CHANGE_PROP_TYPE(PR_RTF_COMPRESSED, PT_UNSPECIFIED));
  172. lHtmlIdx = Util::FindPropInArray(ptrPropTagArray, CHANGE_PROP_TYPE(PR_HTML, PT_UNSPECIFIED));
  173. }
  174. if (!lpPropTagArray || lBodyIdx >= 0 || lRtfIdx >= 0 || lHtmlIdx >= 0) {
  175. /*
  176. * Exchange has a particular way of handling requests for different types of body content. There are three:
  177. *
  178. * - RTF (PR_RTF_COMPRESSED)
  179. * - HTML (PR_HTML or PR_BODY_HTML, these are interchangeable in all cases)
  180. * - Plaintext (PR_BODY)
  181. *
  182. * All of these properties are available (or none at all if there is no body) via OpenProperty() AT ALL TIMES, even if the
  183. * item itself was not saved as that specific type of message. However, the exchange follows the following rules
  184. * when multiple body types are requested in a single GetProps() call:
  185. *
  186. * - Only the 'best fit' property is returned as an actual value, the other properties are returned with PT_ERROR
  187. * - Best fit for plaintext is (in order): PR_BODY, PR_RTF, PR_HTML
  188. * - For RTF messages: PR_RTF, PR_HTML, PR_BODY
  189. * - For HTML messages: PR_HTML, PR_RTF, PR_BODY
  190. * - When GetProps() is called with NULL as the property list, the error value is MAPI_E_NOT_ENOUGH_MEMORY
  191. * - When GetProps() is called with a list of properties, the error value is MAPI_E_NOT_ENOUGH_MEMORY or MAPI_E_NOT_FOUND depending on the following:
  192. * - When the requested property ID is higher than the best-match property, the value is MAPI_E_NOT_FOUND
  193. * - When the requested property ID is lower than the best-match property, the value is MAPI_E_NOT_ENOUGH_MEMORY
  194. *
  195. * Additionally, the normal rules for returning MAPI_E_NOT_ENOUGH_MEMORY apply (ie for large properties).
  196. *
  197. * Example: RTF message, PR_BODY, PR_HTML and PR_RTF_COMPRESSED requested in single GetProps() call:
  198. * returns: PR_BODY -> MAPI_E_NOT_ENOUGH_MEMORY, PR_HTML -> MAPI_E_NOT_FOUND, PR_RTF_COMPRESSED -> actual RTF content
  199. *
  200. * PR_RTF_IN_SYNC is normally always TRUE, EXCEPT if the following is true:
  201. * - Both PR_RTF_COMPRESSED and PR_HTML are requested
  202. * - Actual body type is HTML
  203. *
  204. * This is used to disambiguate the situation in which you request PR_RTF_COMPRESSED and PR_HTML and receive MAPI_E_NOT_ENOUGH_MEMORY for
  205. * both properties (or both are OK but we never do that).
  206. *
  207. * Since the values of the properties depend on the requested property tag set, a property handler cannot be used in this
  208. * case, and therefore the above logic is implemented here.
  209. */
  210. // Find best match property in requested property set
  211. if (lpPropTagArray == NULL)
  212. // No properties specified, best match is always number one choice body property
  213. ulBestMatch = ulBestMatchTable[m_ulBodyType][0];
  214. else {
  215. // Find best match in requested set
  216. for (int i = 0; i < 3; ++i)
  217. if (Util::FindPropInArray(ptrPropTagArray, PROP_TAG(PT_UNSPECIFIED, PROP_ID(ulBestMatchTable[m_ulBodyType][i]))) >= 0) {
  218. ulBestMatch = ulBestMatchTable[m_ulBodyType][i];
  219. break;
  220. }
  221. }
  222. // Remove the non-best bodies from the requested set.
  223. if (lBodyIdx >= 0 && PROP_ID(ulBestMatch) != PROP_ID(PR_BODY))
  224. ptrPropTagArray->aulPropTag[lBodyIdx] = PR_NULL;
  225. if (lRtfIdx >= 0 && PROP_ID(ulBestMatch) != PROP_ID(PR_RTF_COMPRESSED))
  226. ptrPropTagArray->aulPropTag[lRtfIdx] = PR_NULL;
  227. if (lHtmlIdx >= 0 && PROP_ID(ulBestMatch) != PROP_ID(PR_HTML))
  228. ptrPropTagArray->aulPropTag[lHtmlIdx] = PR_NULL;
  229. hr = ECMAPIProp::GetProps(ptrPropTagArray, ulFlags, &cValues, &~ptrPropArray);
  230. if (HR_FAILED(hr))
  231. return hr;
  232. // Set the correct errors on the filtered properties.
  233. if (lBodyIdx >= 0 && PROP_ID(ulBestMatch) != PROP_ID(PR_BODY)) {
  234. ptrPropArray[lBodyIdx].ulPropTag = CHANGE_PROP_TYPE(PR_BODY, PT_ERROR);
  235. ptrPropArray[lBodyIdx].Value.err = MAPI_E_NOT_ENOUGH_MEMORY;
  236. hr = MAPI_W_ERRORS_RETURNED;
  237. }
  238. if (lRtfIdx >= 0 && PROP_ID(ulBestMatch) != PROP_ID(PR_RTF_COMPRESSED)) {
  239. ptrPropArray[lRtfIdx].ulPropTag = CHANGE_PROP_TYPE(PR_RTF_COMPRESSED, PT_ERROR);
  240. if (!lpPropTagArray)
  241. ptrPropArray[lRtfIdx].Value.err = MAPI_E_NOT_ENOUGH_MEMORY;
  242. else
  243. ptrPropArray[lRtfIdx].Value.err = PROP_ID(PR_RTF_COMPRESSED) < PROP_ID(ulBestMatch) ? MAPI_E_NOT_ENOUGH_MEMORY : MAPI_E_NOT_FOUND;
  244. hr = MAPI_W_ERRORS_RETURNED;
  245. }
  246. if (lHtmlIdx >= 0 && PROP_ID(ulBestMatch) != PROP_ID(PR_HTML)) {
  247. ptrPropArray[lHtmlIdx].ulPropTag = CHANGE_PROP_TYPE(PR_HTML, PT_ERROR);
  248. ptrPropArray[lHtmlIdx].Value.err = lpPropTagArray ? MAPI_E_NOT_FOUND : MAPI_E_NOT_ENOUGH_MEMORY;
  249. hr = MAPI_W_ERRORS_RETURNED;
  250. }
  251. // RTF_IN_SYNC should be false only if the message is actually HTML and both RTF and HTML are requested
  252. // (we are indicating that RTF should not be used in this case). Note that PR_RTF_IN_SYNC is normally
  253. // forced to TRUE in our property handler, so we only need to change it to FALSE if needed.
  254. if (lHtmlIdx >= 0 && lRtfIdx >= 0 && m_ulBodyType == bodyTypeHTML) {
  255. LONG lSyncIdx = Util::FindPropInArray(ptrPropTagArray, CHANGE_PROP_TYPE(PR_RTF_IN_SYNC, PT_UNSPECIFIED));
  256. if (lSyncIdx >= 0) {
  257. ptrPropArray[lSyncIdx].ulPropTag = PR_RTF_IN_SYNC;
  258. ptrPropArray[lSyncIdx].Value.b = false;
  259. }
  260. }
  261. } else { // !lpPropTagArray || lBodyIdx >= 0 || lRtfIdx >= 0 || lHtmlIdx >= 0
  262. // lpPropTagArray was specified but no body properties were requested.
  263. hr = ECMAPIProp::GetProps(lpPropTagArray, ulFlags, &cValues, &~ptrPropArray);
  264. if (HR_FAILED(hr))
  265. return hr;
  266. }
  267. } else { // m_ulBodyType != bodyTypeUnknown
  268. // We don't know what out body type is (yet).
  269. hr = ECMAPIProp::GetProps(lpPropTagArray, ulFlags, &cValues, &~ptrPropArray);
  270. if (HR_FAILED(hr))
  271. return hr;
  272. }
  273. *lpcValues = cValues;
  274. *lppPropArray = ptrPropArray.release();
  275. return hr;
  276. }
  277. /**
  278. * Retrieve a body property (PR_BODY, PR_HTML or PR_RTF_COMPRESSED) and make sure it's synchronized
  279. * with the best body returned from the server. This implies that the body might be generated on the
  280. * fly.
  281. *
  282. * @param[in] ulPropTag The proptag of the body to retrieve.
  283. * @param[in] ulFlags Flags
  284. * @param[in] lpBase Base pointer for allocating more memory.
  285. * @param[in,out] lpsPropValue Pointer to an SPropValue that will be updated to contain the
  286. * the body property.
  287. */
  288. HRESULT ECMessage::GetSyncedBodyProp(ULONG ulPropTag, ULONG ulFlags, void *lpBase, LPSPropValue lpsPropValue)
  289. {
  290. HRESULT hr;
  291. if (ulPropTag == PR_BODY_HTML)
  292. ulPropTag = PR_HTML;
  293. hr = HrGetRealProp(ulPropTag, ulFlags, lpBase, lpsPropValue);
  294. if (HR_FAILED(hr))
  295. return hr;
  296. if (PROP_TYPE(lpsPropValue->ulPropTag) == PT_ERROR &&
  297. lpsPropValue->Value.err == MAPI_E_NOT_FOUND &&
  298. m_ulBodyType != bodyTypeUnknown)
  299. {
  300. // If a non-best body was requested, we might need to generate it.
  301. if ((m_ulBodyType == bodyTypePlain && PROP_ID(ulPropTag) == PROP_ID(PR_BODY)) ||
  302. (m_ulBodyType == bodyTypeRTF && PROP_ID(ulPropTag) == PROP_ID(PR_RTF_COMPRESSED)) ||
  303. (m_ulBodyType == bodyTypeHTML && PROP_ID(ulPropTag) == PROP_ID(PR_HTML)))
  304. // Nothing more to do, the best body should be available or generated in HrLoadProps.
  305. return hr;
  306. hr = SyncBody(ulPropTag);
  307. if (hr != hrSuccess)
  308. return hr;
  309. // Retry now the body is generated.
  310. hr = HrGetRealProp(ulPropTag, ulFlags, lpBase, lpsPropValue);
  311. }
  312. return hr;
  313. }
  314. /**
  315. * Synchronize the best body obtained from the server to the requested body.
  316. *
  317. * @param[in] ulPropTag The proptag of the body to synchronize.
  318. */
  319. HRESULT ECMessage::SyncBody(ULONG ulPropTag)
  320. {
  321. HRESULT hr = hrSuccess;
  322. const BOOL fModifyOld = fModify;
  323. if (m_ulBodyType == bodyTypeUnknown) {
  324. // There's nothing to synchronize if we don't know what our best body type is.
  325. hr = MAPI_E_NO_SUPPORT;
  326. goto exit;
  327. }
  328. if (!Util::IsBodyProp(ulPropTag)) {
  329. hr = MAPI_E_INVALID_PARAMETER;
  330. goto exit;
  331. }
  332. // Temporary enable write access
  333. fModify = TRUE;
  334. if (m_ulBodyType == bodyTypePlain) {
  335. if (PROP_ID(ulPropTag) == PROP_ID(PR_RTF_COMPRESSED))
  336. hr = SyncPlainToRtf();
  337. else if (PROP_ID(ulPropTag) == PROP_ID(PR_HTML))
  338. hr = SyncPlainToHtml();
  339. } else if (m_ulBodyType == bodyTypeRTF) {
  340. if (PROP_ID(ulPropTag) == PROP_ID(PR_BODY) || PROP_ID(ulPropTag) == PROP_ID(PR_HTML))
  341. hr = SyncRtf();
  342. } else if (m_ulBodyType == bodyTypeHTML) {
  343. if (PROP_ID(ulPropTag) == PROP_ID(PR_BODY))
  344. hr = SyncHtmlToPlain();
  345. else if (PROP_ID(ulPropTag) == PROP_ID(PR_RTF_COMPRESSED))
  346. hr = SyncHtmlToRtf();
  347. }
  348. exit:
  349. fModify = fModifyOld;
  350. return hr;
  351. }
  352. /**
  353. * Synchronize a plaintext body to an RTF body.
  354. */
  355. HRESULT ECMessage::SyncPlainToRtf()
  356. {
  357. HRESULT hr = hrSuccess;
  358. StreamPtr ptrBodyStream;
  359. StreamPtr ptrCompressedRtfStream;
  360. StreamPtr ptrUncompressedRtfStream;
  361. ULARGE_INTEGER emptySize = {{0,0}};
  362. assert(m_bInhibitSync == false);
  363. m_bInhibitSync = TRUE;
  364. hr = ECMAPIProp::OpenProperty(PR_BODY_W, &IID_IStream, 0, 0, &~ptrBodyStream);
  365. if (hr != hrSuccess)
  366. goto exit;
  367. hr = ECMAPIProp::OpenProperty(PR_RTF_COMPRESSED, &IID_IStream, STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~ptrCompressedRtfStream);
  368. if (hr != hrSuccess)
  369. goto exit;
  370. //Truncate to zero
  371. hr = ptrCompressedRtfStream->SetSize(emptySize);
  372. if (hr != hrSuccess)
  373. goto exit;
  374. hr = WrapCompressedRTFStream(ptrCompressedRtfStream, MAPI_MODIFY, &~ptrUncompressedRtfStream);
  375. if (hr != hrSuccess)
  376. goto exit;
  377. // Convert it now
  378. hr = Util::HrTextToRtf(ptrBodyStream, ptrUncompressedRtfStream);
  379. if (hr != hrSuccess)
  380. goto exit;
  381. // Commit uncompress data
  382. hr = ptrUncompressedRtfStream->Commit(0);
  383. if (hr != hrSuccess)
  384. goto exit;
  385. // Commit compresed data
  386. hr = ptrCompressedRtfStream->Commit(0);
  387. if (hr != hrSuccess)
  388. goto exit;
  389. // We generated this property but don't really want to save it to the server
  390. HrSetCleanProperty(PR_RTF_COMPRESSED);
  391. // and mark it as deleted, since we want the server to remove the old version if this was in the database
  392. m_setDeletedProps.insert(PR_RTF_COMPRESSED);
  393. exit:
  394. m_bInhibitSync = FALSE;
  395. return hr;
  396. }
  397. /**
  398. * Synchronize a plaintext body to an HTML body.
  399. */
  400. HRESULT ECMessage::SyncPlainToHtml()
  401. {
  402. HRESULT hr = hrSuccess;
  403. StreamPtr ptrBodyStream;
  404. unsigned int ulCodePage = 0;
  405. StreamPtr ptrHtmlStream;
  406. ULARGE_INTEGER emptySize = {{0,0}};
  407. assert(m_bInhibitSync == false);
  408. m_bInhibitSync = TRUE;
  409. hr = ECMAPIProp::OpenProperty(PR_BODY_W, &IID_IStream, 0, 0, &~ptrBodyStream);
  410. if (hr != hrSuccess)
  411. goto exit;
  412. hr = ECMAPIProp::OpenProperty(PR_HTML, &IID_IStream, STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~ptrHtmlStream);
  413. if (hr != hrSuccess)
  414. goto exit;
  415. hr = GetCodePage(&ulCodePage);
  416. if (hr != hrSuccess)
  417. goto exit;
  418. hr = ptrHtmlStream->SetSize(emptySize);
  419. if (hr != hrSuccess)
  420. goto exit;
  421. hr = Util::HrTextToHtml(ptrBodyStream, ptrHtmlStream, ulCodePage);
  422. if (hr != hrSuccess)
  423. goto exit;
  424. hr = ptrHtmlStream->Commit(0);
  425. if (hr != hrSuccess)
  426. goto exit;
  427. // We generated this property but don't really want to save it to the server
  428. HrSetCleanProperty(PR_HTML);
  429. // and mark it as deleted, since we want the server to remove the old version if this was in the database
  430. m_setDeletedProps.insert(PR_HTML);
  431. exit:
  432. m_bInhibitSync = FALSE;
  433. return hr;
  434. }
  435. /**
  436. * Synchronize an RTF body to a plaintext and an HTML body.
  437. */
  438. HRESULT ECMessage::SyncRtf()
  439. {
  440. enum eRTFType { RTFTypeOther, RTFTypeFromText, RTFTypeFromHTML};
  441. HRESULT hr = hrSuccess;
  442. string strRTF;
  443. bool bDone = false;
  444. unsigned int ulCodePage = 0;
  445. StreamPtr ptrHTMLStream;
  446. ULONG ulWritten = 0;
  447. eRTFType rtfType = RTFTypeOther;
  448. ULARGE_INTEGER emptySize = {{0,0}};
  449. LARGE_INTEGER moveBegin = {{0,0}};
  450. assert(m_bInhibitSync == false);
  451. m_bInhibitSync = TRUE;
  452. hr = GetRtfData(&strRTF);
  453. if (hr != hrSuccess)
  454. goto exit;
  455. hr = GetCodePage(&ulCodePage);
  456. if (hr != hrSuccess)
  457. goto exit;
  458. hr = ECMAPIProp::OpenProperty(PR_HTML, &IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~ptrHTMLStream);
  459. if (hr != hrSuccess)
  460. goto exit;
  461. hr = ptrHTMLStream->SetSize(emptySize);
  462. if (hr != hrSuccess)
  463. goto exit;
  464. // Determine strategy based on RTF type.
  465. if (isrtfhtml(strRTF.c_str(), strRTF.size()))
  466. rtfType = RTFTypeFromHTML;
  467. else if (isrtftext(strRTF.c_str(), strRTF.size()))
  468. rtfType = RTFTypeFromText;
  469. else
  470. rtfType = RTFTypeOther;
  471. if (rtfType == RTFTypeOther) {
  472. BOOL bUpdated;
  473. hr = RTFSync(&this->m_xMessage, RTF_SYNC_RTF_CHANGED, &bUpdated);
  474. if (hr == hrSuccess) {
  475. StreamPtr ptrBodyStream;
  476. hr = ECMAPIProp::OpenProperty(PR_BODY_W, &IID_IStream, 0, 0, &~ptrBodyStream);
  477. if (hr != hrSuccess)
  478. goto exit;
  479. hr = ptrHTMLStream->SetSize(emptySize);
  480. if (hr != hrSuccess)
  481. goto exit;
  482. hr = Util::HrTextToHtml(ptrBodyStream, ptrHTMLStream, ulCodePage);
  483. if (hr != hrSuccess)
  484. goto exit;
  485. hr = ptrHTMLStream->Commit(0);
  486. if (hr != hrSuccess)
  487. goto exit;
  488. bDone = true;
  489. }
  490. }
  491. if (!bDone) {
  492. string strHTML;
  493. StreamPtr ptrBodyStream;
  494. switch (rtfType) {
  495. case RTFTypeOther:
  496. hr = HrExtractHTMLFromRealRTF(strRTF, strHTML, ulCodePage);
  497. break;
  498. case RTFTypeFromText:
  499. hr = HrExtractHTMLFromTextRTF(strRTF, strHTML, ulCodePage);
  500. break;
  501. case RTFTypeFromHTML:
  502. hr = HrExtractHTMLFromRTF(strRTF, strHTML, ulCodePage);
  503. break;
  504. }
  505. if (hr != hrSuccess)
  506. goto exit;
  507. hr = ptrHTMLStream->Write(strHTML.c_str(), strHTML.size(), &ulWritten);
  508. if (hr != hrSuccess)
  509. goto exit;
  510. hr = ptrHTMLStream->Commit(0);
  511. if (hr != hrSuccess)
  512. goto exit;
  513. hr = ptrHTMLStream->Seek(moveBegin, STREAM_SEEK_SET, NULL);
  514. if (hr != hrSuccess)
  515. goto exit;
  516. hr = ECMAPIProp::OpenProperty(PR_BODY_W, &IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~ptrBodyStream);
  517. if (hr != hrSuccess)
  518. goto exit;
  519. hr = ptrBodyStream->SetSize(emptySize);
  520. if (hr != hrSuccess)
  521. goto exit;
  522. hr = Util::HrHtmlToText(ptrHTMLStream, ptrBodyStream, ulCodePage);
  523. if (hr != hrSuccess)
  524. goto exit;
  525. hr = ptrBodyStream->Commit(0);
  526. if (hr != hrSuccess)
  527. goto exit;
  528. }
  529. if (rtfType == RTFTypeOther) {
  530. // No need to store the HTML.
  531. HrSetCleanProperty(PR_HTML);
  532. // And delete from server in case it changed.
  533. m_setDeletedProps.insert(PR_HTML);
  534. } else if (rtfType == RTFTypeFromText) {
  535. // No need to store anything but the plain text.
  536. HrSetCleanProperty(PR_RTF_COMPRESSED);
  537. HrSetCleanProperty(PR_HTML);
  538. // And delete them both.
  539. m_setDeletedProps.insert(PR_RTF_COMPRESSED);
  540. m_setDeletedProps.insert(PR_HTML);
  541. } else if (rtfType == RTFTypeFromHTML) {
  542. // No need to keep the RTF version
  543. HrSetCleanProperty(PR_RTF_COMPRESSED);
  544. // And delete from server.
  545. m_setDeletedProps.insert(PR_RTF_COMPRESSED);
  546. }
  547. exit:
  548. m_bInhibitSync = FALSE;
  549. return hr;
  550. }
  551. /**
  552. * Synchronize an HTML body to a plaintext body.
  553. */
  554. HRESULT ECMessage::SyncHtmlToPlain()
  555. {
  556. HRESULT hr = hrSuccess;
  557. StreamPtr ptrHtmlStream;
  558. StreamPtr ptrBodyStream;
  559. unsigned int ulCodePage;
  560. ULARGE_INTEGER emptySize = {{0,0}};
  561. assert(m_bInhibitSync == FALSE);
  562. m_bInhibitSync = TRUE;
  563. hr = ECMAPIProp::OpenProperty(PR_HTML, &IID_IStream, 0, 0, &~ptrHtmlStream);
  564. if (hr != hrSuccess)
  565. goto exit;
  566. hr = ECMAPIProp::OpenProperty(PR_BODY_W, &IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~ptrBodyStream);
  567. if (hr != hrSuccess)
  568. goto exit;
  569. hr = ptrBodyStream->SetSize(emptySize);
  570. if (hr != hrSuccess)
  571. goto exit;
  572. hr = GetCodePage(&ulCodePage);
  573. if (hr != hrSuccess)
  574. goto exit;
  575. hr = Util::HrHtmlToText(ptrHtmlStream, ptrBodyStream, ulCodePage);
  576. if (hr != hrSuccess)
  577. goto exit;
  578. hr = ptrBodyStream->Commit(0);
  579. if (hr != hrSuccess)
  580. goto exit;
  581. exit:
  582. m_bInhibitSync = FALSE;
  583. return hr;
  584. }
  585. /**
  586. * Synchronize an HTML body to an RTF body.
  587. */
  588. HRESULT ECMessage::SyncHtmlToRtf()
  589. {
  590. HRESULT hr = hrSuccess;
  591. StreamPtr ptrHtmlStream;
  592. StreamPtr ptrRtfCompressedStream;
  593. StreamPtr ptrRtfUncompressedStream;
  594. unsigned int ulCodePage;
  595. ULARGE_INTEGER emptySize = {{0,0}};
  596. assert(!m_bInhibitSync);
  597. m_bInhibitSync = TRUE;
  598. hr = ECMAPIProp::OpenProperty(PR_HTML, &IID_IStream, 0, 0, &~ptrHtmlStream);
  599. if (hr != hrSuccess)
  600. goto exit;
  601. hr = ECMAPIProp::OpenProperty(PR_RTF_COMPRESSED, &IID_IStream, STGM_TRANSACTED, MAPI_CREATE|MAPI_MODIFY, &~ptrRtfCompressedStream);
  602. if (hr != hrSuccess)
  603. goto exit;
  604. hr = ptrRtfCompressedStream->SetSize(emptySize);
  605. if (hr != hrSuccess)
  606. goto exit;
  607. hr = WrapCompressedRTFStream(ptrRtfCompressedStream, MAPI_MODIFY, &~ptrRtfUncompressedStream);
  608. if (hr != hrSuccess)
  609. goto exit;
  610. hr = GetCodePage(&ulCodePage);
  611. if (hr != hrSuccess)
  612. goto exit;
  613. // Convert it now
  614. hr = Util::HrHtmlToRtf(ptrHtmlStream, ptrRtfUncompressedStream, ulCodePage);
  615. if (hr != hrSuccess)
  616. goto exit;
  617. // Commit uncompress data
  618. hr = ptrRtfUncompressedStream->Commit(0);
  619. if (hr != hrSuccess)
  620. goto exit;
  621. // Commit compresed data
  622. hr = ptrRtfCompressedStream->Commit(0);
  623. if (hr != hrSuccess)
  624. goto exit;
  625. // We generated this property but don't really want to save it to the server
  626. HrSetCleanProperty(PR_RTF_COMPRESSED);
  627. // and mark it as deleted, since we want the server to remove the old version if this was in the database
  628. m_setDeletedProps.insert(PR_RTF_COMPRESSED);
  629. exit:
  630. m_bInhibitSync = FALSE;
  631. return hr;
  632. }
  633. HRESULT ECMessage::GetPropList(ULONG ulFlags, LPSPropTagArray *lppPropTagArray)
  634. {
  635. HRESULT hr = hrSuccess;
  636. const eBodyType ulBodyTypeSaved = m_ulBodyType;
  637. SPropTagArrayPtr ptrPropTagArray;
  638. SPropTagArrayPtr ptrPropTagArrayMod;
  639. bool bHaveBody;
  640. bool bHaveRtf;
  641. bool bHaveHtml;
  642. m_ulBodyType = bodyTypeUnknown; // Make sure no bodies are generated when attempts are made to open them to check the error code if any.
  643. hr = ECMAPIProp::GetPropList(ulFlags, &~ptrPropTagArray);
  644. if (hr != hrSuccess)
  645. goto exit;
  646. // Because the body type was set to unknown, ECMAPIProp::GetPropList does not return the proptags of the bodies that can be
  647. // generated unless they were already generated.
  648. // So we need to check which body properties are included and if at least one is, we need to make sure all of them are.
  649. bHaveBody = Util::FindPropInArray(ptrPropTagArray, CHANGE_PROP_TYPE(PR_BODY, PT_UNSPECIFIED)) >= 0;
  650. bHaveRtf = Util::FindPropInArray(ptrPropTagArray, PR_RTF_COMPRESSED) >= 0;
  651. bHaveHtml = Util::FindPropInArray(ptrPropTagArray, PR_HTML) >= 0;
  652. if ((bHaveBody && bHaveRtf && bHaveHtml) ||
  653. (!bHaveBody && !bHaveRtf && !bHaveHtml))
  654. {
  655. // Nothing more to do
  656. *lppPropTagArray = ptrPropTagArray.release();
  657. goto exit;
  658. }
  659. // We have at least one body prop. Determine which tags to add.
  660. hr = ECAllocateBuffer(CbNewSPropTagArray(ptrPropTagArray->cValues + 2), &~ptrPropTagArrayMod);
  661. if (hr != hrSuccess)
  662. goto exit;
  663. ptrPropTagArrayMod->cValues = ptrPropTagArray->cValues;
  664. memcpy(ptrPropTagArrayMod->aulPropTag, ptrPropTagArray->aulPropTag, sizeof(*ptrPropTagArrayMod->aulPropTag) * ptrPropTagArrayMod->cValues);
  665. // All three booleans can't be NULL at the same time, so two additions max.
  666. if (!bHaveBody)
  667. ptrPropTagArrayMod->aulPropTag[ptrPropTagArrayMod->cValues++] = (ulFlags & MAPI_UNICODE ? PR_BODY_W : PR_BODY_A);
  668. if (!bHaveRtf)
  669. ptrPropTagArrayMod->aulPropTag[ptrPropTagArrayMod->cValues++] = PR_RTF_COMPRESSED;
  670. if (!bHaveHtml)
  671. ptrPropTagArrayMod->aulPropTag[ptrPropTagArrayMod->cValues++] = PR_HTML;
  672. *lppPropTagArray = ptrPropTagArrayMod.release();
  673. exit:
  674. m_ulBodyType = ulBodyTypeSaved;
  675. return hr;
  676. }
  677. HRESULT ECMessage::OpenProperty(ULONG ulPropTag, LPCIID lpiid, ULONG ulInterfaceOptions, ULONG ulFlags, LPUNKNOWN *lppUnk)
  678. {
  679. HRESULT hr = MAPI_E_INTERFACE_NOT_SUPPORTED;
  680. if (lpiid == NULL)
  681. return MAPI_E_INVALID_PARAMETER;
  682. //FIXME: Support the flags ?
  683. if(ulPropTag == PR_MESSAGE_ATTACHMENTS) {
  684. if(*lpiid == IID_IMAPITable)
  685. hr = GetAttachmentTable(ulInterfaceOptions, (LPMAPITABLE*)lppUnk);
  686. } else if(ulPropTag == PR_MESSAGE_RECIPIENTS) {
  687. if (*lpiid == IID_IMAPITable)
  688. hr = GetRecipientTable(ulInterfaceOptions, (LPMAPITABLE*)lppUnk);
  689. } else {
  690. // Workaround for support html in outlook 2000/xp
  691. if(ulPropTag == PR_BODY_HTML)
  692. ulPropTag = PR_HTML;
  693. hr = ECMAPIProp::OpenProperty(ulPropTag, lpiid, ulInterfaceOptions, ulFlags, lppUnk);
  694. if (hr == MAPI_E_NOT_FOUND && m_ulBodyType != bodyTypeUnknown && Util::IsBodyProp(ulPropTag)) {
  695. hr = SyncBody(ulPropTag);
  696. if (hr != hrSuccess)
  697. return hr;
  698. // Retry now the body is generated.
  699. hr = ECMAPIProp::OpenProperty(ulPropTag, lpiid, ulInterfaceOptions, ulFlags, lppUnk);
  700. }
  701. }
  702. return hr;
  703. }
  704. HRESULT ECMessage::GetAttachmentTable(ULONG ulFlags, LPMAPITABLE *lppTable)
  705. {
  706. HRESULT hr = hrSuccess;
  707. LPSPropValue lpPropID = NULL;
  708. LPSPropValue lpPropType = NULL;
  709. memory_ptr<SPropTagArray> lpPropTagArray;
  710. scoped_rlock lock(m_hMutexMAPIObject);
  711. if(lstProps == NULL) {
  712. hr = HrLoadProps();
  713. if (hr != hrSuccess)
  714. return hr;
  715. if (lstProps == nullptr)
  716. return MAPI_E_CALL_FAILED;
  717. }
  718. if (this->lpAttachments == NULL) {
  719. hr = Util::HrCopyUnicodePropTagArray(ulFlags,
  720. sPropAttachColumns, &~lpPropTagArray);
  721. if(hr != hrSuccess)
  722. return hr;
  723. hr = ECMemTable::Create(lpPropTagArray, PR_ATTACH_NUM, &this->lpAttachments);
  724. if(hr != hrSuccess)
  725. return hr;
  726. // This code is resembles the table-copying code in GetRecipientTable, but we do some slightly different
  727. // processing on the data that we receive from the table. Basically, data is copied to the attachment
  728. // table received from the server through m_sMapiObject, but the PR_ATTACH_NUM is re-generated locally
  729. if (!fNew) {
  730. // existing message has "table" in m_sMapiObject data
  731. for (const auto &obj : m_sMapiObject->lstChildren) {
  732. if (obj->ulObjType != MAPI_ATTACH)
  733. continue;
  734. if (obj->bDelete)
  735. continue;
  736. this->ulNextAttUniqueId = obj->ulUniqueId > this->ulNextAttUniqueId ? obj->ulUniqueId : this->ulNextAttUniqueId;
  737. ++this->ulNextAttUniqueId;
  738. ULONG ulProps = obj->lstProperties.size();
  739. ecmem_ptr<SPropValue> lpProps;
  740. SPropValue sKeyProp;
  741. ULONG i;
  742. // +1 for maybe missing PR_ATTACH_NUM property
  743. // +1 for maybe missing PR_OBJECT_TYPE property
  744. hr = ECAllocateBuffer(sizeof(SPropValue) * (ulProps + 2), &~lpProps);
  745. if (hr != hrSuccess)
  746. return hr;
  747. lpPropID = NULL;
  748. lpPropType = NULL;
  749. i = 0;
  750. for (const auto &pv : obj->lstProperties) {
  751. pv.CopyToByRef(&lpProps[i]);
  752. if (lpProps[i].ulPropTag == PR_ATTACH_NUM) {
  753. lpPropID = &lpProps[i];
  754. } else if (lpProps[i].ulPropTag == PR_OBJECT_TYPE) {
  755. lpPropType = &lpProps[i];
  756. } else if (PROP_ID(lpProps[i].ulPropTag) == PROP_ID(PR_ATTACH_DATA_OBJ)) {
  757. lpProps[i].ulPropTag = CHANGE_PROP_TYPE(lpProps[i].ulPropTag, PT_ERROR);
  758. lpProps[i].Value.err = MAPI_E_NOT_ENOUGH_MEMORY;
  759. } else if (PROP_TYPE(lpProps[i].ulPropTag) == PT_BINARY && lpProps[i].Value.bin.cb > MAX_TABLE_PROPSIZE) {
  760. lpProps[i].ulPropTag = CHANGE_PROP_TYPE(lpProps[i].ulPropTag, PT_ERROR);
  761. lpProps[i].Value.err = MAPI_E_NOT_ENOUGH_MEMORY;
  762. }
  763. ++i;
  764. }
  765. if (lpPropID == NULL) {
  766. ++ulProps;
  767. lpPropID = &lpProps[i++];
  768. }
  769. lpPropID->ulPropTag = PR_ATTACH_NUM;
  770. lpPropID->Value.ul = obj->ulUniqueId; // use uniqueid from "recount" code in WSMAPIPropStorage::desoapertize()
  771. if (lpPropType == NULL) {
  772. ++ulProps;
  773. lpPropType = &lpProps[i++];
  774. }
  775. lpPropType->ulPropTag = PR_OBJECT_TYPE;
  776. lpPropType->Value.ul = MAPI_ATTACH;
  777. sKeyProp.ulPropTag = PR_EC_HIERARCHYID;
  778. sKeyProp.Value.ul = obj->ulObjId;
  779. hr = lpAttachments->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, &sKeyProp, lpProps, i);
  780. if (hr != hrSuccess)
  781. return hr; // continue?
  782. }
  783. // since we just loaded the table, all enties are clean (actually not required for attachments, but it doesn't hurt)
  784. hr = lpAttachments->HrSetClean();
  785. if (hr != hrSuccess)
  786. return hr;
  787. } // !new == empty table
  788. }
  789. if (this->lpAttachments == nullptr)
  790. return MAPI_E_CALL_FAILED;
  791. object_ptr<ECMemTableView> lpView;
  792. hr = lpAttachments->HrGetView(createLocaleFromName(""),
  793. ulFlags & MAPI_UNICODE, &~lpView);
  794. if(hr != hrSuccess)
  795. return hr;
  796. return lpView->QueryInterface(IID_IMAPITable,
  797. reinterpret_cast<void **>(lppTable));
  798. }
  799. HRESULT ECMessage::OpenAttach(ULONG ulAttachmentNum, LPCIID lpInterface, ULONG ulFlags, LPATTACH *lppAttach)
  800. {
  801. HRESULT hr = hrSuccess;
  802. object_ptr<ECAttach> lpAttach;
  803. object_ptr<IECPropStorage> lpParentStorage;
  804. SPropValue sID;
  805. ecmem_ptr<SPropValue> lpObjId;
  806. ULONG ulObjId;
  807. if(this->lpAttachments == NULL) {
  808. object_ptr<IMAPITable> lpTable;
  809. hr = this->GetAttachmentTable(fMapiUnicode, &~lpTable);
  810. if(hr != hrSuccess)
  811. return hr;
  812. }
  813. if (this->lpAttachments == nullptr)
  814. return MAPI_E_CALL_FAILED;
  815. hr = ECAttach::Create(this->GetMsgStore(), MAPI_ATTACH, TRUE, ulAttachmentNum, m_lpRoot, &~lpAttach);
  816. if(hr != hrSuccess)
  817. return hr;
  818. sID.ulPropTag = PR_ATTACH_NUM;
  819. sID.Value.ul = ulAttachmentNum;
  820. if (lpAttachments->HrGetRowID(&sID, &~lpObjId) == hrSuccess)
  821. ulObjId = lpObjId->Value.ul;
  822. else
  823. ulObjId = 0;
  824. hr = this->GetMsgStore()->lpTransport->HrOpenParentStorage(this, ulAttachmentNum, ulObjId, this->lpStorage->GetServerStorage(), &~lpParentStorage);
  825. if(hr != hrSuccess)
  826. return hr;
  827. hr = lpAttach->HrSetPropStorage(lpParentStorage, TRUE);
  828. if(hr != hrSuccess)
  829. return hr;
  830. hr = lpAttach->QueryInterface(IID_IAttachment, (void **)lppAttach);
  831. // Register the object as a child of ours
  832. AddChild(lpAttach);
  833. return hr;
  834. }
  835. HRESULT ECMessage::CreateAttach(LPCIID lpInterface, ULONG ulFlags, ULONG *lpulAttachmentNum, LPATTACH *lppAttach)
  836. {
  837. return CreateAttach(lpInterface, ulFlags, ECAttachFactory(), lpulAttachmentNum, lppAttach);
  838. }
  839. HRESULT ECMessage::CreateAttach(LPCIID lpInterface, ULONG ulFlags, const IAttachFactory &refFactory, ULONG *lpulAttachmentNum, LPATTACH *lppAttach)
  840. {
  841. HRESULT hr = hrSuccess;
  842. SPropValue sID;
  843. object_ptr<IECPropStorage> lpStorage;
  844. if(this->lpAttachments == NULL) {
  845. object_ptr<IMAPITable> lpTable;
  846. hr = this->GetAttachmentTable(fMapiUnicode, &~lpTable);
  847. if(hr != hrSuccess)
  848. return hr;
  849. }
  850. if (this->lpAttachments == nullptr)
  851. return MAPI_E_CALL_FAILED;
  852. object_ptr<ECAttach> lpAttach;
  853. hr = refFactory.Create(this->GetMsgStore(), MAPI_ATTACH, TRUE,
  854. this->ulNextAttUniqueId, m_lpRoot, &~lpAttach);
  855. if(hr != hrSuccess)
  856. return hr;
  857. hr = lpAttach->HrLoadEmptyProps();
  858. if(hr != hrSuccess)
  859. return hr;
  860. sID.ulPropTag = PR_ATTACH_NUM;
  861. sID.Value.ul = this->ulNextAttUniqueId;
  862. hr = this->GetMsgStore()->lpTransport->HrOpenParentStorage(this, this->ulNextAttUniqueId, 0, NULL, &~lpStorage);
  863. if(hr != hrSuccess)
  864. return hr;
  865. hr = lpAttach->HrSetPropStorage(lpStorage, FALSE);
  866. if(hr != hrSuccess)
  867. return hr;
  868. hr = lpAttach->SetProps(1, &sID, NULL);
  869. if(hr != hrSuccess)
  870. return hr;
  871. hr = lpAttach->QueryInterface(IID_IAttachment, (void **)lppAttach);
  872. AddChild(lpAttach);
  873. *lpulAttachmentNum = sID.Value.ul;
  874. // successfully created attachment, so increment counter for the next
  875. ++this->ulNextAttUniqueId;
  876. return hr;
  877. }
  878. HRESULT ECMessage::DeleteAttach(ULONG ulAttachmentNum, ULONG ulUIParam, LPMAPIPROGRESS lpProgress, ULONG ulFlags)
  879. {
  880. HRESULT hr;
  881. SPropValue sPropID;
  882. if(this->lpAttachments == NULL) {
  883. object_ptr<IMAPITable> lpTable;
  884. hr = this->GetAttachmentTable(fMapiUnicode, &~lpTable);
  885. if(hr != hrSuccess)
  886. return hr;
  887. }
  888. if (this->lpAttachments == NULL)
  889. return MAPI_E_CALL_FAILED;
  890. sPropID.ulPropTag = PR_ATTACH_NUM;
  891. sPropID.Value.ul = ulAttachmentNum;
  892. hr = this->lpAttachments->HrModifyRow(ECKeyTable::TABLE_ROW_DELETE, NULL, &sPropID, 1);
  893. if (hr !=hrSuccess)
  894. return hr;
  895. // the object is deleted from the child list when SaveChanges is called, which calls SyncAttachments()
  896. return hrSuccess;
  897. }
  898. HRESULT ECMessage::GetRecipientTable(ULONG ulFlags, LPMAPITABLE *lppTable)
  899. {
  900. HRESULT hr = hrSuccess;
  901. memory_ptr<SPropTagArray> lpPropTagArray;
  902. scoped_rlock lock(m_hMutexMAPIObject);
  903. if(lstProps == NULL) {
  904. hr = HrLoadProps();
  905. if (hr != hrSuccess)
  906. return hr;
  907. if (lstProps == nullptr)
  908. return MAPI_E_CALL_FAILED;
  909. }
  910. if (this->lpRecips == NULL) {
  911. hr = Util::HrCopyUnicodePropTagArray(ulFlags,
  912. sPropRecipColumns, &~lpPropTagArray);
  913. if(hr != hrSuccess)
  914. return hr;
  915. hr = ECMemTable::Create(lpPropTagArray, PR_ROWID, &lpRecips);
  916. if(hr != hrSuccess)
  917. return hr;
  918. // What we do here is that we reconstruct a recipient table from the m_sMapiObject data, and then process it in two ways:
  919. // 1. Remove PR_ROWID values and replace them with client-side values
  920. // 2. Replace PR_EC_CONTACT_ENTRYID with PR_ENTRYID in the table
  921. // This means that the PR_ENTRYID value from the original recipient is not actually in the table (only
  922. // in the lpID value passed to HrModifyRow)
  923. // Get the existing table for this message (there is none if the message is unsaved)
  924. if (!fNew) {
  925. // existing message has "table" in m_sMapiObject data
  926. for (const auto &obj : m_sMapiObject->lstChildren) {
  927. // The only valid types are MAPI_MAILUSER and MAPI_DISTLIST. However some MAPI clients put in other
  928. // values as object type. We know about the existence of MAPI_ATTACH as another valid subtype for
  929. // Messages, so we'll skip those, treat MAPI_DISTLIST as MAPI_DISTLIST and anything else as
  930. // MAPI_MAILUSER.
  931. if (obj->ulObjType == MAPI_ATTACH)
  932. continue;
  933. if (obj->bDelete)
  934. continue;
  935. this->ulNextRecipUniqueId = obj->ulUniqueId > this->ulNextRecipUniqueId ? obj->ulUniqueId : this->ulNextRecipUniqueId;
  936. ++this->ulNextRecipUniqueId;
  937. ULONG ulProps = obj->lstProperties.size();
  938. ecmem_ptr<SPropValue> lpProps;
  939. SPropValue sKeyProp;
  940. ULONG i = 0;
  941. LPSPropValue lpPropID = NULL;
  942. LPSPropValue lpPropObjType = NULL;
  943. // +1 for maybe missing PR_ROWID property
  944. // +1 for maybe missing PR_OBJECT_TYPE property
  945. hr = ECAllocateBuffer(sizeof(SPropValue) * (ulProps + 2), &~lpProps);
  946. if (hr != hrSuccess)
  947. return hr;
  948. for (const auto &pv : obj->lstProperties) {
  949. pv.CopyToByRef(&lpProps[i]);
  950. if (lpProps[i].ulPropTag == PR_ROWID)
  951. lpPropID = &lpProps[i];
  952. else if (lpProps[i].ulPropTag == PR_OBJECT_TYPE)
  953. lpPropObjType = &lpProps[i];
  954. else if (lpProps[i].ulPropTag == PR_EC_CONTACT_ENTRYID)
  955. // rename to PR_ENTRYID
  956. lpProps[i].ulPropTag = PR_ENTRYID;
  957. ++i;
  958. }
  959. if (lpPropID == NULL) {
  960. ++ulProps;
  961. lpPropID = &lpProps[i++];
  962. }
  963. lpPropID->ulPropTag = PR_ROWID;
  964. lpPropID->Value.ul = obj->ulUniqueId; // use uniqueid from "recount" code in WSMAPIPropStorage::ECSoapObjectToMapiObject()
  965. if (lpPropObjType == NULL) {
  966. ++ulProps;
  967. lpPropObjType = &lpProps[i++];
  968. }
  969. lpPropObjType->ulPropTag = PR_OBJECT_TYPE;
  970. lpPropObjType->Value.ul = obj->ulObjType;
  971. sKeyProp.ulPropTag = PR_EC_HIERARCHYID;
  972. sKeyProp.Value.ul = obj->ulObjId;
  973. hr = lpRecips->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, &sKeyProp, lpProps, i);
  974. if (hr != hrSuccess)
  975. return hr;
  976. }
  977. // since we just loaded the table, all enties are clean
  978. hr = lpRecips->HrSetClean();
  979. if (hr != hrSuccess)
  980. return hr;
  981. } // !fNew
  982. }
  983. object_ptr<ECMemTableView> lpView;
  984. hr = lpRecips->HrGetView(createLocaleFromName(""),
  985. ulFlags & MAPI_UNICODE, &~lpView);
  986. if(hr != hrSuccess)
  987. return hr;
  988. return lpView->QueryInterface(IID_IMAPITable,
  989. reinterpret_cast<void **>(lppTable));
  990. }
  991. /*
  992. * This is not as easy as it seems. This is how we handle modifyrecipients:
  993. *
  994. * If the user specified a PR_ROWID, we always use their ROW ID
  995. * If the user specifies no PR_ROWID, we generate one, starting at 1 and going upward
  996. * MODIFY and ADD are the same
  997. *
  998. * This makes the following scenario possible:
  999. *
  1000. * - Add row without id (row id 1 is generated)
  1001. * - Add row without id (row id 2 is generated)
  1002. * - Add row with id 5 (row 5 is added, we now have row 1,2,5)
  1003. * - Add row with id 1 (row 1 is now modified, possibly without the caller wanting this)
  1004. *
  1005. * However, this seem to be what is required by outlook, as for example ExpandRecips from
  1006. * the support object assumes the row IDs to stay the same when it does ModifyRecipients(0, lpMods)
  1007. * so we can't generate the IDs whenever ADD or 0 is specified.
  1008. */
  1009. HRESULT ECMessage::ModifyRecipients(ULONG ulFlags, const ADRLIST *lpMods)
  1010. {
  1011. HRESULT hr = hrSuccess;
  1012. ecmem_ptr<SPropValue> lpRecipProps;
  1013. ULONG cValuesRecipProps = 0;
  1014. SPropValue sPropAdd[2];
  1015. SPropValue sKeyProp;
  1016. unsigned int i = 0;
  1017. if (lpMods == nullptr)
  1018. return MAPI_E_INVALID_PARAMETER;
  1019. if (!fModify)
  1020. return MAPI_E_NO_ACCESS;
  1021. // Load the recipients table object
  1022. if(lpRecips == NULL) {
  1023. object_ptr<IMAPITable> lpTable;
  1024. hr = GetRecipientTable(fMapiUnicode, &~lpTable);
  1025. if(hr != hrSuccess)
  1026. return hr;
  1027. }
  1028. if (lpRecips == nullptr)
  1029. return MAPI_E_CALL_FAILED;
  1030. if(ulFlags == 0) {
  1031. hr = lpRecips->HrDeleteAll();
  1032. if(hr != hrSuccess)
  1033. return hr;
  1034. ulNextRecipUniqueId = 0;
  1035. }
  1036. for (i = 0; i < lpMods->cEntries; ++i) {
  1037. if(ulFlags & MODRECIP_ADD || ulFlags == 0) {
  1038. // Add a new PR_ROWID
  1039. sPropAdd[0].ulPropTag = PR_ROWID;
  1040. sPropAdd[0].Value.ul = this->ulNextRecipUniqueId++;
  1041. // Add a PR_INSTANCE_KEY which is equal to the row id
  1042. sPropAdd[1].ulPropTag = PR_INSTANCE_KEY;
  1043. sPropAdd[1].Value.bin.cb = sizeof(ULONG);
  1044. sPropAdd[1].Value.bin.lpb = (unsigned char *)&sPropAdd[0].Value.ul;
  1045. hr = Util::HrMergePropertyArrays(lpMods->aEntries[i].rgPropVals, lpMods->aEntries[i].cValues, sPropAdd, 2, &+lpRecipProps, &cValuesRecipProps);
  1046. if (hr != hrSuccess)
  1047. continue;
  1048. sKeyProp.ulPropTag = PR_EC_HIERARCHYID;
  1049. sKeyProp.Value.ul = 0;
  1050. // Add the new row
  1051. hr = lpRecips->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, &sKeyProp, lpRecipProps, cValuesRecipProps);
  1052. lpRecipProps.reset();
  1053. } else if(ulFlags & MODRECIP_MODIFY) {
  1054. // Simply update the existing row, leave the PR_EC_HIERARCHY key prop intact.
  1055. hr = lpRecips->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, NULL, lpMods->aEntries[i].rgPropVals, lpMods->aEntries[i].cValues);
  1056. } else if(ulFlags & MODRECIP_REMOVE) {
  1057. hr = lpRecips->HrModifyRow(ECKeyTable::TABLE_ROW_DELETE, NULL, lpMods->aEntries[i].rgPropVals, lpMods->aEntries[i].cValues);
  1058. }
  1059. if(hr != hrSuccess)
  1060. return hr;
  1061. }
  1062. m_bRecipsDirty = TRUE;
  1063. return hrSuccess;
  1064. }
  1065. HRESULT ECMessage::SubmitMessage(ULONG ulFlags)
  1066. {
  1067. HRESULT hr = hrSuccess;
  1068. SizedSPropTagArray(1, sPropTagArray);
  1069. ULONG cValue = 0;
  1070. ULONG ulRepCount = 0;
  1071. ULONG ulPreprocessFlags = 0;
  1072. ULONG ulSubmitFlag = 0;
  1073. ecmem_ptr<SPropValue> lpsPropArray;
  1074. object_ptr<IMAPITable> lpRecipientTable;
  1075. ecmem_ptr<SPropValue> lpRecip;
  1076. ULONG cRecip = 0;
  1077. SizedADRLIST(1, sRowSetRecip);
  1078. SPropValue sPropResponsibility;
  1079. FILETIME ft;
  1080. // Get message flag to check for resubmit. PR_MESSAGE_FLAGS
  1081. sPropTagArray.cValues = 1;
  1082. sPropTagArray.aulPropTag[0] = PR_MESSAGE_FLAGS;
  1083. hr = ECMAPIProp::GetProps(sPropTagArray, 0, &cValue, &~lpsPropArray);
  1084. if(HR_FAILED(hr))
  1085. return hr;
  1086. if(lpsPropArray->ulPropTag == PR_MESSAGE_FLAGS) {
  1087. // Re-set 'unsent' as it is obviously not sent if we're submitting it ... This allows you to send a message
  1088. // multiple times, but only if the client calls SubmitMessage multiple times.
  1089. lpsPropArray->Value.ul |= MSGFLAG_UNSENT;
  1090. hr = this->SetProps(1, lpsPropArray, NULL);
  1091. if(hr != hrSuccess)
  1092. return hr;
  1093. }
  1094. // Get the recipientslist
  1095. hr = this->GetRecipientTable(fMapiUnicode, &~lpRecipientTable);
  1096. if(hr != hrSuccess)
  1097. return hr;
  1098. // Check if recipientslist is empty
  1099. hr = lpRecipientTable->GetRowCount(0, &ulRepCount);
  1100. if(hr != hrSuccess)
  1101. return hr;
  1102. if (ulRepCount == 0)
  1103. return MAPI_E_NO_RECIPIENTS;
  1104. // Step through recipient list, set PR_RESPONSIBILITY to FALSE for all recipients
  1105. while(TRUE){
  1106. rowset_ptr lpsRow;
  1107. hr = lpRecipientTable->QueryRows(1, 0, &~lpsRow);
  1108. if (hr != hrSuccess)
  1109. return hr;
  1110. if (lpsRow->cRows == 0)
  1111. break;
  1112. sPropResponsibility.ulPropTag = PR_RESPONSIBILITY;
  1113. sPropResponsibility.Value.b = FALSE;
  1114. // Set PR_RESPONSIBILITY
  1115. hr = Util::HrAddToPropertyArray(lpsRow->aRow[0].lpProps,
  1116. lpsRow->aRow[0].cValues, &sPropResponsibility, &+lpRecip, &cRecip);
  1117. if(hr != hrSuccess)
  1118. return hr;
  1119. sRowSetRecip.cEntries = 1;
  1120. sRowSetRecip.aEntries[0].rgPropVals = lpRecip;
  1121. sRowSetRecip.aEntries[0].cValues = cRecip;
  1122. if(lpsRow->aRow[0].cValues > 1){
  1123. hr = this->ModifyRecipients(MODRECIP_MODIFY, sRowSetRecip);
  1124. if (hr != hrSuccess)
  1125. return hr;
  1126. }
  1127. lpRecip.reset();
  1128. }
  1129. // Get the time to add to the message as PR_CLIENT_SUBMIT_TIME
  1130. GetSystemTimeAsFileTime(&ft);
  1131. hr = ECAllocateBuffer(sizeof(SPropValue) * 2, &~lpsPropArray);
  1132. if (hr != hrSuccess)
  1133. return hr;
  1134. lpsPropArray[0].ulPropTag = PR_CLIENT_SUBMIT_TIME;
  1135. lpsPropArray[0].Value.ft = ft;
  1136. lpsPropArray[1].ulPropTag = PR_MESSAGE_DELIVERY_TIME;
  1137. lpsPropArray[1].Value.ft = ft;
  1138. hr = SetProps(2, lpsPropArray, NULL);
  1139. if (hr != hrSuccess)
  1140. return hr;
  1141. // Resolve recipients
  1142. hr = this->GetMsgStore()->lpSupport->ExpandRecips(&this->m_xMessage, &ulPreprocessFlags);
  1143. if (hr != hrSuccess)
  1144. return hr;
  1145. if (this->GetMsgStore()->IsOfflineStore())
  1146. ulPreprocessFlags |= NEEDS_SPOOLER;
  1147. // Setup PR_SUBMIT_FLAGS
  1148. if (ulPreprocessFlags & NEEDS_PREPROCESSING)
  1149. ulSubmitFlag = SUBMITFLAG_PREPROCESS;
  1150. if (ulPreprocessFlags & NEEDS_SPOOLER)
  1151. ulSubmitFlag = 0L;
  1152. hr = ECAllocateBuffer(sizeof(SPropValue), &~lpsPropArray);
  1153. if (hr != hrSuccess)
  1154. return hr;
  1155. lpsPropArray[0].ulPropTag = PR_SUBMIT_FLAGS;
  1156. lpsPropArray[0].Value.l = ulSubmitFlag;
  1157. hr = SetProps(1, lpsPropArray, NULL);
  1158. if (hr != hrSuccess)
  1159. return hr;
  1160. // All done, save changes
  1161. hr = SaveChanges(KEEP_OPEN_READWRITE);
  1162. if(hr != hrSuccess)
  1163. return hr;
  1164. // We look al ulPreprocessFlags to see whether to submit the message via the
  1165. // spooler or not
  1166. if(ulPreprocessFlags & NEEDS_SPOOLER) {
  1167. TRACE_MAPI(TRACE_ENTRY, "Submitting through local queue, flags", "%d", ulPreprocessFlags);
  1168. // Add this message into the local outgoing queue
  1169. hr = this->GetMsgStore()->lpTransport->HrSubmitMessage(this->m_cbEntryId, this->m_lpEntryId, EC_SUBMIT_LOCAL);
  1170. } else {
  1171. TRACE_MAPI(TRACE_ENTRY, "Submitting through master queue, flags", "%d", ulPreprocessFlags);
  1172. // Add the message to the master outgoing queue, and request the spooler to DoSentMail()
  1173. hr = this->GetMsgStore()->lpTransport->HrSubmitMessage(this->m_cbEntryId, this->m_lpEntryId, EC_SUBMIT_MASTER | EC_SUBMIT_DOSENTMAIL);
  1174. }
  1175. return hr;
  1176. }
  1177. HRESULT ECMessage::SetReadFlag(ULONG ulFlags)
  1178. {
  1179. HRESULT hr = hrSuccess;
  1180. ecmem_ptr<SPropValue> lpReadReceiptRequest;
  1181. memory_ptr<SPropValue> lpPropFlags;
  1182. memory_ptr<SPropValue> lpsPropUserName;
  1183. SPropValue sProp;
  1184. object_ptr<IMAPIFolder> lpRootFolder;
  1185. object_ptr<IMessage> lpNewMessage, lpThisMessage;
  1186. ULONG ulObjType = 0;
  1187. ULONG cValues = 0;
  1188. ULONG cbStoreID = 0;
  1189. memory_ptr<ENTRYID> lpStoreID;
  1190. object_ptr<IMsgStore> lpDefMsgStore;
  1191. if((ulFlags &~ (CLEAR_READ_FLAG | CLEAR_NRN_PENDING | CLEAR_RN_PENDING | GENERATE_RECEIPT_ONLY | MAPI_DEFERRED_ERRORS | SUPPRESS_RECEIPT)) != 0 ||
  1192. (ulFlags & (SUPPRESS_RECEIPT | CLEAR_READ_FLAG)) == (SUPPRESS_RECEIPT | CLEAR_READ_FLAG)||
  1193. (ulFlags & (SUPPRESS_RECEIPT | CLEAR_READ_FLAG | GENERATE_RECEIPT_ONLY)) == (SUPPRESS_RECEIPT | CLEAR_READ_FLAG | GENERATE_RECEIPT_ONLY) ||
  1194. (ulFlags & (CLEAR_READ_FLAG | GENERATE_RECEIPT_ONLY)) == (CLEAR_READ_FLAG | GENERATE_RECEIPT_ONLY) )
  1195. return MAPI_E_INVALID_PARAMETER;
  1196. if (m_lpParentID != nullptr)
  1197. // Unsaved message, ignore (FIXME ?)
  1198. return hrSuccess;
  1199. // see if read receipts are requested
  1200. static constexpr const SizedSPropTagArray(2, proptags) =
  1201. {2, {PR_MESSAGE_FLAGS, PR_READ_RECEIPT_REQUESTED}};
  1202. hr = ECMAPIProp::GetProps(proptags, 0, &cValues, &~lpReadReceiptRequest);
  1203. if(hr == hrSuccess && (!(ulFlags&(SUPPRESS_RECEIPT|CLEAR_READ_FLAG | CLEAR_NRN_PENDING | CLEAR_RN_PENDING)) || (ulFlags&GENERATE_RECEIPT_ONLY )) &&
  1204. lpReadReceiptRequest[1].Value.b == TRUE && ((lpReadReceiptRequest[0].Value.ul & MSGFLAG_RN_PENDING) || (lpReadReceiptRequest[0].Value.ul & MSGFLAG_NRN_PENDING)))
  1205. {
  1206. hr = QueryInterface(IID_IMessage, &~lpThisMessage);
  1207. if (hr != hrSuccess)
  1208. return hr;
  1209. if((ulFlags & (GENERATE_RECEIPT_ONLY | SUPPRESS_RECEIPT)) == (GENERATE_RECEIPT_ONLY | SUPPRESS_RECEIPT) )
  1210. {
  1211. sProp.ulPropTag = PR_READ_RECEIPT_REQUESTED;
  1212. sProp.Value.b = FALSE;
  1213. hr = HrSetOneProp(lpThisMessage, &sProp);
  1214. if (hr != hrSuccess)
  1215. return hr;
  1216. hr = lpThisMessage->SaveChanges(KEEP_OPEN_READWRITE);
  1217. if (hr != hrSuccess)
  1218. return hr;
  1219. }else {
  1220. // Open the default store, by using the username property
  1221. hr = HrGetOneProp(&GetMsgStore()->m_xMsgStore, PR_USER_NAME, &~lpsPropUserName);
  1222. if (hr != hrSuccess)
  1223. return hr;
  1224. hr = GetMsgStore()->CreateStoreEntryID(nullptr, lpsPropUserName->Value.LPSZ, fMapiUnicode, &cbStoreID, &~lpStoreID);
  1225. if (hr != hrSuccess)
  1226. return hr;
  1227. hr = GetMsgStore()->lpSupport->OpenEntry(cbStoreID, lpStoreID, nullptr, MAPI_MODIFY, &ulObjType, &~lpDefMsgStore);
  1228. if (hr != hrSuccess)
  1229. return hr;
  1230. // Open the root folder of the default store to create a new message
  1231. hr = lpDefMsgStore->OpenEntry(0, nullptr, nullptr, MAPI_MODIFY, &ulObjType, &~lpRootFolder);
  1232. if (hr != hrSuccess)
  1233. return hr;
  1234. hr = lpRootFolder->CreateMessage(nullptr, 0, &~lpNewMessage);
  1235. if (hr != hrSuccess)
  1236. return hr;
  1237. hr = ClientUtil::ReadReceipt(0, lpThisMessage, &+lpNewMessage);
  1238. if(hr != hrSuccess)
  1239. return hr;
  1240. hr = lpNewMessage->SubmitMessage(FORCE_SUBMIT);
  1241. if(hr != hrSuccess)
  1242. return hr;
  1243. // Oke, everything is fine, so now remove MSGFLAG_RN_PENDING and MSGFLAG_NRN_PENDING from PR_MESSAGE_FLAGS
  1244. // Sent CLEAR_NRN_PENDING and CLEAR_RN_PENDING for remove those properties
  1245. ulFlags |= CLEAR_NRN_PENDING | CLEAR_RN_PENDING;
  1246. }
  1247. }
  1248. hr = this->GetMsgStore()->lpTransport->HrSetReadFlag(this->m_cbEntryId, this->m_lpEntryId, ulFlags, 0);
  1249. if(hr != hrSuccess)
  1250. return hr;
  1251. // Server update OK, change local flags also
  1252. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpPropFlags);
  1253. if (hr != hrSuccess)
  1254. return hr;
  1255. hr = HrGetRealProp(PR_MESSAGE_FLAGS, ulFlags, lpPropFlags, lpPropFlags);
  1256. if(hr != hrSuccess)
  1257. return hr;
  1258. if (ulFlags & CLEAR_READ_FLAG)
  1259. lpPropFlags->Value.ul &= ~MSGFLAG_READ;
  1260. else
  1261. lpPropFlags->Value.ul |= MSGFLAG_READ;
  1262. return HrSetRealProp(lpPropFlags);
  1263. }
  1264. /**
  1265. * Synchronizes this object's PR_DISPLAY_* properties from the
  1266. * contents of the recipient table. They are pushed to the server
  1267. * on save.
  1268. */
  1269. HRESULT ECMessage::SyncRecips()
  1270. {
  1271. HRESULT hr = hrSuccess;
  1272. std::wstring wstrTo;
  1273. std::wstring wstrCc;
  1274. std::wstring wstrBcc;
  1275. SPropValue sPropRecip;
  1276. object_ptr<IMAPITable> lpTable;
  1277. static constexpr const SizedSPropTagArray(2, sPropDisplay) =
  1278. {2, {PR_RECIPIENT_TYPE, PR_DISPLAY_NAME_W}};
  1279. if (this->lpRecips) {
  1280. hr = GetRecipientTable(fMapiUnicode, &~lpTable);
  1281. if (hr != hrSuccess)
  1282. return hr;
  1283. hr = lpTable->SetColumns(sPropDisplay, 0);
  1284. while (TRUE) {
  1285. rowset_ptr lpRows;
  1286. hr = lpTable->QueryRows(1, 0, &~lpRows);
  1287. if (hr != hrSuccess || lpRows->cRows != 1)
  1288. break;
  1289. if (lpRows->aRow[0].lpProps[0].ulPropTag == PR_RECIPIENT_TYPE && lpRows->aRow[0].lpProps[0].Value.ul == MAPI_TO) {
  1290. if (lpRows->aRow[0].lpProps[1].ulPropTag == PR_DISPLAY_NAME_W) {
  1291. if (wstrTo.length() > 0)
  1292. wstrTo += L"; ";
  1293. wstrTo += lpRows->aRow[0].lpProps[1].Value.lpszW;
  1294. }
  1295. }
  1296. else if (lpRows->aRow[0].lpProps[0].ulPropTag == PR_RECIPIENT_TYPE && lpRows->aRow[0].lpProps[0].Value.ul == MAPI_CC) {
  1297. if (lpRows->aRow[0].lpProps[1].ulPropTag == PR_DISPLAY_NAME_W) {
  1298. if (wstrCc.length() > 0)
  1299. wstrCc += L"; ";
  1300. wstrCc += lpRows->aRow[0].lpProps[1].Value.lpszW;
  1301. }
  1302. }
  1303. else if (lpRows->aRow[0].lpProps[0].ulPropTag == PR_RECIPIENT_TYPE && lpRows->aRow[0].lpProps[0].Value.ul == MAPI_BCC) {
  1304. if (lpRows->aRow[0].lpProps[1].ulPropTag == PR_DISPLAY_NAME_W) {
  1305. if (wstrBcc.length() > 0)
  1306. wstrBcc += L"; ";
  1307. wstrBcc += lpRows->aRow[0].lpProps[1].Value.lpszW;
  1308. }
  1309. }
  1310. }
  1311. sPropRecip.ulPropTag = PR_DISPLAY_TO_W;
  1312. sPropRecip.Value.lpszW = (WCHAR *)wstrTo.c_str();
  1313. HrSetRealProp(&sPropRecip);
  1314. sPropRecip.ulPropTag = PR_DISPLAY_CC_W;
  1315. sPropRecip.Value.lpszW = (WCHAR *)wstrCc.c_str();
  1316. HrSetRealProp(&sPropRecip);
  1317. sPropRecip.ulPropTag = PR_DISPLAY_BCC_W;
  1318. sPropRecip.Value.lpszW = (WCHAR *)wstrBcc.c_str();
  1319. HrSetRealProp(&sPropRecip);
  1320. }
  1321. m_bRecipsDirty = FALSE;
  1322. return hr;
  1323. }
  1324. HRESULT ECMessage::SaveRecips()
  1325. {
  1326. HRESULT hr = hrSuccess;
  1327. rowset_ptr lpRowSet;
  1328. ecmem_ptr<SPropValue> lpObjIDs;
  1329. ecmem_ptr<ULONG> lpulStatus;
  1330. unsigned int i = 0,
  1331. j = 0;
  1332. ULONG ulRealObjType;
  1333. scoped_rlock lock(m_hMutexMAPIObject);
  1334. // Get any changes and set it in the child list of this message
  1335. hr = lpRecips->HrGetAllWithStatus(&~lpRowSet, &~lpObjIDs, &~lpulStatus);
  1336. if (hr != hrSuccess)
  1337. return hr;
  1338. for (i = 0; i < lpRowSet->cRows; ++i) {
  1339. MAPIOBJECT *mo = NULL;
  1340. // Get the right object type for a DistList
  1341. auto lpObjType = PCpropFindProp(lpRowSet->aRow[i].lpProps, lpRowSet->aRow[i].cValues, PR_OBJECT_TYPE);
  1342. if(lpObjType != NULL)
  1343. ulRealObjType = lpObjType->Value.ul; // MAPI_MAILUSER or MAPI_DISTLIST
  1344. else
  1345. ulRealObjType = MAPI_MAILUSER; // add in list?
  1346. auto lpRowId = PCpropFindProp(lpRowSet->aRow[i].lpProps, lpRowSet->aRow[i].cValues, PR_ROWID); // unique value of recipient
  1347. if (!lpRowId) {
  1348. assert(lpRowId != NULL);
  1349. continue;
  1350. }
  1351. AllocNewMapiObject(lpRowId->Value.ul, lpObjIDs[i].Value.ul, ulRealObjType, &mo);
  1352. // Move any PR_ENTRYIDs to PR_EC_CONTACT_ENTRYID
  1353. auto lpEntryID = PpropFindProp(lpRowSet->aRow[i].lpProps, lpRowSet->aRow[i].cValues, PR_ENTRYID);
  1354. if(lpEntryID)
  1355. lpEntryID->ulPropTag = PR_EC_CONTACT_ENTRYID;
  1356. if (lpulStatus[i] == ECROW_MODIFIED || lpulStatus[i] == ECROW_ADDED) {
  1357. mo->bChanged = true;
  1358. for (j = 0; j < lpRowSet->aRow[i].cValues; ++j)
  1359. if(PROP_TYPE(lpRowSet->aRow[i].lpProps[j].ulPropTag) != PT_NULL) {
  1360. mo->lstModified.push_back(ECProperty(&lpRowSet->aRow[i].lpProps[j]));
  1361. // as in ECGenericProp.cpp, we also save the properties to the known list,
  1362. // since this is used when we reload the object from memory.
  1363. mo->lstProperties.push_back(ECProperty(&lpRowSet->aRow[i].lpProps[j]));
  1364. }
  1365. } else if (lpulStatus[i] == ECROW_DELETED) {
  1366. mo->bDelete = true;
  1367. } else {
  1368. // ECROW_NORMAL, untouched recipient
  1369. for (j = 0; j < lpRowSet->aRow[i].cValues; ++j)
  1370. if(PROP_TYPE(lpRowSet->aRow[i].lpProps[j].ulPropTag) != PT_NULL)
  1371. mo->lstProperties.push_back(ECProperty(&lpRowSet->aRow[i].lpProps[j]));
  1372. }
  1373. // find old recipient in child list, and remove if present
  1374. auto iterSObj = m_sMapiObject->lstChildren.find(mo);
  1375. if (iterSObj != m_sMapiObject->lstChildren.cend()) {
  1376. FreeMapiObject(*iterSObj);
  1377. m_sMapiObject->lstChildren.erase(iterSObj);
  1378. }
  1379. m_sMapiObject->lstChildren.insert(mo);
  1380. }
  1381. return lpRecips->HrSetClean();
  1382. }
  1383. void ECMessage::RecursiveMarkDelete(MAPIOBJECT *lpObj) {
  1384. lpObj->bDelete = true;
  1385. lpObj->lstDeleted.clear();
  1386. lpObj->lstAvailable.clear();
  1387. lpObj->lstModified.clear();
  1388. lpObj->lstProperties.clear();
  1389. for (const auto &obj : lpObj->lstChildren)
  1390. RecursiveMarkDelete(obj);
  1391. }
  1392. BOOL ECMessage::HasAttachment()
  1393. {
  1394. HRESULT hr = hrSuccess;
  1395. BOOL bRet = TRUE;
  1396. ECMapiObjects::const_iterator iterObjects;
  1397. scoped_rlock lock(m_hMutexMAPIObject);
  1398. if(lstProps == NULL) {
  1399. hr = HrLoadProps();
  1400. if (hr != hrSuccess)
  1401. goto exit;
  1402. if(lstProps == NULL) {
  1403. hr = MAPI_E_CALL_FAILED;
  1404. goto exit;
  1405. }
  1406. }
  1407. for (iterObjects = m_sMapiObject->lstChildren.cbegin();
  1408. iterObjects != m_sMapiObject->lstChildren.cend(); ++iterObjects)
  1409. if ((*iterObjects)->ulObjType == MAPI_ATTACH)
  1410. break;
  1411. bRet = iterObjects != m_sMapiObject->lstChildren.cend();
  1412. exit:
  1413. if(hr != hrSuccess)
  1414. bRet = FALSE;
  1415. return bRet;
  1416. }
  1417. // Syncs the Attachment table to the child list in the saved object
  1418. HRESULT ECMessage::SyncAttachments()
  1419. {
  1420. HRESULT hr = hrSuccess;
  1421. rowset_ptr lpRowSet;
  1422. ecmem_ptr<SPropValue> lpObjIDs;
  1423. // LPSPropValue lpAttachNum = NULL;
  1424. ecmem_ptr<ULONG> lpulStatus;
  1425. unsigned int i = 0;
  1426. // LPSPropValue lpObjType = NULL;
  1427. scoped_rlock lock(m_hMutexMAPIObject);
  1428. // Get any changes and set it in the child list of this message
  1429. // Although we only need to know the deleted attachments, I also need to know the PR_ATTACH_NUM, which is in the rowset
  1430. hr = lpAttachments->HrGetAllWithStatus(&~lpRowSet, &~lpObjIDs, &~lpulStatus);
  1431. if (hr != hrSuccess)
  1432. return hr;
  1433. for (i = 0; i < lpRowSet->cRows; ++i) {
  1434. if (lpulStatus[i] != ECROW_DELETED)
  1435. continue;
  1436. auto lpObjType = PCpropFindProp(lpRowSet->aRow[i].lpProps, lpRowSet->aRow[i].cValues, PR_OBJECT_TYPE);
  1437. if(lpObjType == NULL || lpObjType->Value.ul != MAPI_ATTACH)
  1438. continue;
  1439. auto lpAttachNum = PCpropFindProp(lpRowSet->aRow[i].lpProps, lpRowSet->aRow[i].cValues, PR_ATTACH_NUM); // unique value of attachment
  1440. if (!lpAttachNum) {
  1441. assert(lpAttachNum != NULL);
  1442. continue;
  1443. }
  1444. // delete complete attachment
  1445. MAPIOBJECT find(lpObjType->Value.ul, lpAttachNum->Value.ul);
  1446. auto iterSObj = m_sMapiObject->lstChildren.find(&find);
  1447. if (iterSObj != m_sMapiObject->lstChildren.cend())
  1448. RecursiveMarkDelete(*iterSObj);
  1449. }
  1450. return lpAttachments->HrSetClean();
  1451. }
  1452. HRESULT ECMessage::UpdateTable(ECMemTable *lpTable, ULONG ulObjType, ULONG ulObjKeyProp) {
  1453. HRESULT hr = hrSuccess;
  1454. SPropValue sKeyProp;
  1455. SPropValue sUniqueProp;
  1456. ULONG cAllValues = 0;
  1457. ULONG cValues = 0;
  1458. ULONG ulProps = 0;
  1459. ULONG i = 0;
  1460. scoped_rlock lock(m_hMutexMAPIObject);
  1461. if (m_sMapiObject == nullptr)
  1462. return MAPI_E_INVALID_PARAMETER;
  1463. // update hierarchy id in table
  1464. for (const auto &obj : m_sMapiObject->lstChildren) {
  1465. memory_ptr<SPropValue> lpProps, lpNewProps, lpAllProps;
  1466. if (obj->ulObjType != ulObjType)
  1467. continue;
  1468. sUniqueProp.ulPropTag = ulObjKeyProp;
  1469. sUniqueProp.Value.ul = obj->ulUniqueId;
  1470. sKeyProp.ulPropTag = PR_EC_HIERARCHYID;
  1471. sKeyProp.Value.ul = obj->ulObjId;
  1472. hr = lpTable->HrUpdateRowID(&sKeyProp, &sUniqueProp, 1);
  1473. if (hr != hrSuccess)
  1474. return hr;
  1475. // put new server props in table too
  1476. ulProps = obj->lstProperties.size();
  1477. if (ulProps == 0)
  1478. continue;
  1479. // retrieve old row from table
  1480. hr = lpTable->HrGetRowData(&sUniqueProp, &cValues, &~lpProps);
  1481. if (hr != hrSuccess)
  1482. return hr;
  1483. // add new props
  1484. hr = MAPIAllocateBuffer(sizeof(SPropValue) * ulProps, &~lpNewProps);
  1485. if (hr != hrSuccess)
  1486. return hr;
  1487. i = 0;
  1488. for (const auto &pv : obj->lstProperties) {
  1489. pv.CopyToByRef(&lpNewProps[i]);
  1490. if (PROP_ID(lpNewProps[i].ulPropTag) == PROP_ID(PR_ATTACH_DATA_OBJ)) {
  1491. lpNewProps[i].ulPropTag = CHANGE_PROP_TYPE(lpNewProps[i].ulPropTag, PT_ERROR);
  1492. lpNewProps[i].Value.err = MAPI_E_NOT_ENOUGH_MEMORY;
  1493. } else if (PROP_TYPE(lpNewProps[i].ulPropTag) == PT_BINARY && lpNewProps[i].Value.bin.cb > MAX_TABLE_PROPSIZE) {
  1494. lpNewProps[i].ulPropTag = CHANGE_PROP_TYPE(lpNewProps[i].ulPropTag, PT_ERROR);
  1495. lpNewProps[i].Value.err = MAPI_E_NOT_ENOUGH_MEMORY;
  1496. }
  1497. ++i;
  1498. }
  1499. hr = Util::HrMergePropertyArrays(lpProps, cValues, lpNewProps, ulProps, &~lpAllProps, &cAllValues);
  1500. if (hr != hrSuccess)
  1501. return hr;
  1502. hr = lpTable->HrModifyRow(ECKeyTable::TABLE_ROW_MODIFY, &sKeyProp, lpAllProps, cAllValues);
  1503. if (hr != hrSuccess)
  1504. return hr;
  1505. }
  1506. return lpTable->HrSetClean();
  1507. }
  1508. HRESULT ECMessage::SaveChanges(ULONG ulFlags)
  1509. {
  1510. HRESULT hr = hrSuccess;
  1511. ecmem_ptr<SPropValue> lpsPropMessageFlags;
  1512. ULONG cValues = 0;
  1513. scoped_rlock lock(m_hMutexMAPIObject);
  1514. // could not have modified (easy way out of my bug)
  1515. if (!fModify)
  1516. return MAPI_E_NO_ACCESS;
  1517. // nothing changed -> no need to save
  1518. if (this->lstProps == NULL)
  1519. return hr;
  1520. assert(m_sMapiObject != NULL); // the actual bug .. keep open on submessage
  1521. if (this->lpRecips) {
  1522. hr = SaveRecips();
  1523. if (hr != hrSuccess)
  1524. return hr;
  1525. // Synchronize PR_DISPLAY_* ... FIXME should we do this after each ModifyRecipients ?
  1526. SyncRecips();
  1527. }
  1528. if (this->lpAttachments) {
  1529. // set deleted attachments in saved child list
  1530. hr = SyncAttachments();
  1531. if (hr != hrSuccess)
  1532. return hr;
  1533. }
  1534. // Property change of a new item
  1535. if (fNew && this->GetMsgStore()->IsSpooler() == TRUE) {
  1536. static constexpr const SizedSPropTagArray(1, proptag) = {1, {PR_MESSAGE_FLAGS}};
  1537. hr = ECMAPIProp::GetProps(proptag, 0, &cValues, &~lpsPropMessageFlags);
  1538. if(hr != hrSuccess)
  1539. return hr;
  1540. lpsPropMessageFlags->ulPropTag = PR_MESSAGE_FLAGS;
  1541. lpsPropMessageFlags->Value.l &= ~(MSGFLAG_READ|MSGFLAG_UNSENT);
  1542. lpsPropMessageFlags->Value.l |= MSGFLAG_UNMODIFIED;
  1543. hr = SetProps(1, lpsPropMessageFlags, NULL);
  1544. if(hr != hrSuccess)
  1545. return hr;
  1546. }
  1547. // don't re-sync bodies that are returned from server
  1548. assert(!m_bInhibitSync);
  1549. m_bInhibitSync = TRUE;
  1550. hr = ECMAPIProp::SaveChanges(ulFlags);
  1551. m_bInhibitSync = FALSE;
  1552. m_bExplicitSubjectPrefix = FALSE;
  1553. if(hr != hrSuccess)
  1554. return hr;
  1555. // resync recip and attachment table, because of hierarchy IDs, only on actual saved object
  1556. if (m_sMapiObject && m_bEmbedded == false) {
  1557. if (lpRecips) {
  1558. hr = UpdateTable(lpRecips, MAPI_MAILUSER, PR_ROWID);
  1559. if(hr != hrSuccess)
  1560. return hr;
  1561. hr = UpdateTable(lpRecips, MAPI_DISTLIST, PR_ROWID);
  1562. if(hr != hrSuccess)
  1563. return hr;
  1564. }
  1565. if (lpAttachments) {
  1566. hr = UpdateTable(lpAttachments, MAPI_ATTACH, PR_ATTACH_NUM);
  1567. if(hr != hrSuccess)
  1568. return hr;
  1569. }
  1570. }
  1571. return hrSuccess;
  1572. }
  1573. /**
  1574. * Sync PR_SUBJECT, PR_SUBJECT_PREFIX
  1575. */
  1576. HRESULT ECMessage::SyncSubject()
  1577. {
  1578. HRESULT hr = hrSuccess;
  1579. HRESULT hr1 = hrSuccess;
  1580. HRESULT hr2 = hrSuccess;
  1581. BOOL bDirtySubject = FALSE;
  1582. BOOL bDirtySubjectPrefix = FALSE;
  1583. ecmem_ptr<SPropValue> lpPropArray;
  1584. ULONG cValues = 0;
  1585. WCHAR* lpszColon = NULL;
  1586. WCHAR* lpszEnd = NULL;
  1587. int sizePrefix1 = 0;
  1588. static constexpr const SizedSPropTagArray(2, sPropSubjects) =
  1589. {2, {PR_SUBJECT_W, PR_SUBJECT_PREFIX_W}};
  1590. hr1 = IsPropDirty(CHANGE_PROP_TYPE(PR_SUBJECT, PT_UNSPECIFIED), &bDirtySubject);
  1591. hr2 = IsPropDirty(CHANGE_PROP_TYPE(PR_SUBJECT_PREFIX, PT_UNSPECIFIED), &bDirtySubjectPrefix);
  1592. // if both not present or not dirty
  1593. if( (hr1 != hrSuccess && hr2 != hrSuccess) || (hr1 == hr2 && bDirtySubject == FALSE && bDirtySubjectPrefix == FALSE) )
  1594. return hr;
  1595. // If subject is deleted but the prefix is not, delete it
  1596. if(hr1 != hrSuccess && hr2 == hrSuccess)
  1597. return HrDeleteRealProp(CHANGE_PROP_TYPE(PR_SUBJECT_PREFIX, PT_UNSPECIFIED), FALSE);
  1598. // Check if subject and prefix in sync
  1599. hr = ECMAPIProp::GetProps(sPropSubjects, 0, &cValues, &~lpPropArray);
  1600. if(HR_FAILED(hr))
  1601. return hr;
  1602. if(lpPropArray[0].ulPropTag == PR_SUBJECT_W)
  1603. lpszColon = wcschr(lpPropArray[0].Value.lpszW, L':');
  1604. if(lpszColon == NULL) {
  1605. //Set emtpy PR_SUBJECT_PREFIX
  1606. lpPropArray[1].ulPropTag = PR_SUBJECT_PREFIX_W;
  1607. lpPropArray[1].Value.lpszW = const_cast<wchar_t *>(L"");
  1608. hr = HrSetRealProp(&lpPropArray[1]);
  1609. } else {
  1610. sizePrefix1 = lpszColon - lpPropArray[0].Value.lpszW + 1;
  1611. // synchronized PR_SUBJECT_PREFIX
  1612. lpPropArray[1].ulPropTag = PR_SUBJECT_PREFIX_W; // If PROP_TYPE(lpPropArray[1].ulPropTag) == PT_ERROR, we lose that info here.
  1613. if (sizePrefix1 > 1 && sizePrefix1 <= 4)
  1614. {
  1615. if (lpPropArray[0].Value.lpszW[sizePrefix1] == L' ')
  1616. lpPropArray[0].Value.lpszW[sizePrefix1+1] = 0; // with space "fwd: "
  1617. else
  1618. lpPropArray[0].Value.lpszW[sizePrefix1] = 0; // "fwd:"
  1619. assert(lpPropArray[0].Value.lpszW[sizePrefix1-1] == L':');
  1620. lpPropArray[1].Value.lpszW = lpPropArray[0].Value.lpszW;
  1621. wcstol(lpPropArray[1].Value.lpszW, &lpszEnd, 10);
  1622. if (lpszEnd == lpszColon)
  1623. lpPropArray[1].Value.lpszW = const_cast<wchar_t *>(L""); // skip a numeric prefix
  1624. } else
  1625. lpPropArray[1].Value.lpszW = const_cast<wchar_t *>(L""); // emtpy PR_SUBJECT_PREFIX
  1626. hr = HrSetRealProp(&lpPropArray[1]);
  1627. // PR_SUBJECT_PREFIX and PR_SUBJECT are synchronized
  1628. }
  1629. return hr;
  1630. }
  1631. // Override IMAPIProp::SetProps
  1632. HRESULT ECMessage::SetProps(ULONG cValues, const SPropValue *lpPropArray,
  1633. SPropProblemArray **lppProblems)
  1634. {
  1635. HRESULT hr = hrSuccess;
  1636. const SPropValue *pvalSubject, *pvalSubjectPrefix;
  1637. const SPropValue *pvalRtf, *pvalHtml, *pvalBody;
  1638. const BOOL bInhibitSyncOld = m_bInhibitSync;
  1639. m_bInhibitSync = TRUE; // We want to override the logic in ECMessage::HrSetRealProp.
  1640. // Send to IMAPIProp first
  1641. hr = ECMAPIProp::SetProps(cValues, lpPropArray, lppProblems);
  1642. if (hr != hrSuccess)
  1643. goto exit;
  1644. m_bInhibitSync = bInhibitSyncOld;
  1645. /* We only sync the subject (like a PST does) in the following conditions:
  1646. * 1) PR_SUBJECT is modified, and PR_SUBJECT_PREFIX was not set
  1647. * 2) PR_SUBJECT is modified, and PR_SUBJECT_PREFIX was modified by a previous SyncSubject() call
  1648. * If the caller ever does a SetProps on the PR_SUBJECT_PREFIX itself, we must never touch it ourselves again, until SaveChanges().
  1649. */
  1650. pvalSubject = PCpropFindProp(lpPropArray, cValues, CHANGE_PROP_TYPE(PR_SUBJECT, PT_UNSPECIFIED));
  1651. pvalSubjectPrefix = PCpropFindProp(lpPropArray, cValues, CHANGE_PROP_TYPE(PR_SUBJECT_PREFIX, PT_UNSPECIFIED));
  1652. if (pvalSubjectPrefix)
  1653. m_bExplicitSubjectPrefix = TRUE;
  1654. if (pvalSubject && m_bExplicitSubjectPrefix == FALSE)
  1655. SyncSubject();
  1656. // Now, sync RTF
  1657. pvalRtf = PCpropFindProp(lpPropArray, cValues, PR_RTF_COMPRESSED);
  1658. pvalHtml = PCpropFindProp(lpPropArray, cValues, PROP_TAG(PT_UNSPECIFIED, PROP_ID(PR_BODY_HTML)) );
  1659. pvalBody = PCpropFindProp(lpPropArray, cValues, PROP_TAG(PT_UNSPECIFIED, PROP_ID(PR_BODY)) );
  1660. // IF the user sets both the body and the RTF, assume RTF overrides
  1661. if (pvalRtf) {
  1662. m_ulBodyType = bodyTypeUnknown; // Make sure GetBodyType doesn't use the cached value
  1663. GetBodyType(&m_ulBodyType);
  1664. SyncRtf();
  1665. } else if (pvalHtml) {
  1666. m_ulBodyType = bodyTypeHTML;
  1667. SyncHtmlToPlain();
  1668. HrDeleteRealProp(PR_RTF_COMPRESSED, FALSE);
  1669. } else if(pvalBody) {
  1670. m_ulBodyType = bodyTypePlain;
  1671. HrDeleteRealProp(PR_RTF_COMPRESSED, FALSE);
  1672. HrDeleteRealProp(PR_HTML, FALSE);
  1673. }
  1674. exit:
  1675. m_bInhibitSync = bInhibitSyncOld;
  1676. return hr;
  1677. }
  1678. HRESULT ECMessage::DeleteProps(const SPropTagArray *lpPropTagArray,
  1679. SPropProblemArray **lppProblems)
  1680. {
  1681. HRESULT hr;
  1682. SizedSPropTagArray(1, sSubjectPrefix) =
  1683. {1, {CHANGE_PROP_TYPE(PR_SUBJECT_PREFIX, PT_UNSPECIFIED)}};
  1684. // Send to IMAPIProp first
  1685. hr = ECMAPIProp::DeleteProps(lpPropTagArray, lppProblems);
  1686. if (FAILED(hr))
  1687. return hr;
  1688. // If the PR_SUBJECT is removed and we generated the prefix, we need to remove that property too.
  1689. if (m_bExplicitSubjectPrefix == FALSE && Util::FindPropInArray(lpPropTagArray, CHANGE_PROP_TYPE(PR_SUBJECT, PT_UNSPECIFIED)) >= 0)
  1690. ECMAPIProp::DeleteProps(sSubjectPrefix, NULL);
  1691. // If an explicit prefix was set and now removed, we must sync it again on the next SetProps of the subject
  1692. if (m_bExplicitSubjectPrefix == TRUE && Util::FindPropInArray(lpPropTagArray, CHANGE_PROP_TYPE(PR_SUBJECT_PREFIX, PT_UNSPECIFIED)) >= 0)
  1693. m_bExplicitSubjectPrefix = FALSE;
  1694. return hrSuccess;
  1695. }
  1696. HRESULT ECMessage::TableRowGetProp(void* lpProvider, struct propVal *lpsPropValSrc, LPSPropValue lpsPropValDst, void **lpBase, ULONG ulType)
  1697. {
  1698. HRESULT hr = hrSuccess;
  1699. auto lpMsgStore = static_cast<ECMsgStore *>(lpProvider);
  1700. if (lpsPropValSrc->ulPropTag != PR_SOURCE_KEY)
  1701. return MAPI_E_NOT_FOUND;
  1702. if ((lpMsgStore->m_ulProfileFlags & EC_PROFILE_FLAGS_TRUNCATE_SOURCEKEY) &&
  1703. lpsPropValSrc->Value.bin->__size > 22) {
  1704. lpsPropValSrc->Value.bin->__size = 22;
  1705. lpsPropValSrc->Value.bin->__ptr[lpsPropValSrc->Value.bin->__size-1] |= 0x80; // Set top bit
  1706. hr = CopySOAPPropValToMAPIPropVal(lpsPropValDst, lpsPropValSrc, lpBase);
  1707. } else {
  1708. hr = MAPI_E_NOT_FOUND;
  1709. }
  1710. return hr;
  1711. }
  1712. HRESULT ECMessage::GetPropHandler(ULONG ulPropTag, void* lpProvider, ULONG ulFlags, LPSPropValue lpsPropValue, void *lpParam, void *lpBase)
  1713. {
  1714. HRESULT hr = hrSuccess;
  1715. unsigned int ulSize = 0;
  1716. LPBYTE lpData = NULL;
  1717. auto lpMessage = static_cast<ECMessage *>(lpParam);
  1718. switch(PROP_ID(ulPropTag)) {
  1719. case PROP_ID(PR_RTF_IN_SYNC):
  1720. lpsPropValue->ulPropTag = PR_RTF_IN_SYNC;
  1721. lpsPropValue->Value.ul = TRUE; // Always in sync because we sync internally
  1722. break;
  1723. case PROP_ID(PR_HASATTACH):
  1724. lpsPropValue->ulPropTag = PR_HASATTACH;
  1725. lpsPropValue->Value.b = lpMessage->HasAttachment();
  1726. break;
  1727. case PROP_ID(PR_ASSOCIATED):
  1728. hr = lpMessage->HrGetRealProp(PR_MESSAGE_FLAGS, ulFlags, lpBase, lpsPropValue);
  1729. if(hr != hrSuccess) {
  1730. hr = hrSuccess;
  1731. lpsPropValue->ulPropTag = PR_ASSOCIATED;
  1732. lpsPropValue->Value.b = false;
  1733. } else {
  1734. lpsPropValue->ulPropTag = PR_ASSOCIATED;
  1735. lpsPropValue->Value.b = !!(lpsPropValue->Value.ul & MSGFLAG_ASSOCIATED);
  1736. }
  1737. break;
  1738. case PROP_ID(PR_MESSAGE_FLAGS):
  1739. {
  1740. hr = lpMessage->HrGetRealProp(PR_MESSAGE_FLAGS, ulFlags, lpBase, lpsPropValue);
  1741. if(hr != hrSuccess) {
  1742. hr = hrSuccess;
  1743. lpsPropValue->ulPropTag = PR_MESSAGE_FLAGS;
  1744. lpsPropValue->Value.ul = MSGFLAG_READ;
  1745. }
  1746. // Force MSGFLAG_HASATTACH to the correct value
  1747. lpsPropValue->Value.ul = (lpsPropValue->Value.ul & ~MSGFLAG_HASATTACH) | (lpMessage->HasAttachment() ? MSGFLAG_HASATTACH : 0);
  1748. break;
  1749. }
  1750. case PROP_ID(PR_NORMALIZED_SUBJECT):
  1751. hr = lpMessage->HrGetRealProp(CHANGE_PROP_TYPE(PR_SUBJECT, PROP_TYPE(ulPropTag)), ulFlags, lpBase, lpsPropValue);
  1752. if (hr != hrSuccess) {
  1753. // change PR_SUBJECT in PR_NORMALIZED_SUBJECT
  1754. lpsPropValue->ulPropTag = CHANGE_PROP_TYPE(PR_NORMALIZED_SUBJECT, PT_ERROR);
  1755. break;
  1756. }
  1757. if (PROP_TYPE(ulPropTag) == PT_UNICODE) {
  1758. lpsPropValue->ulPropTag = PR_NORMALIZED_SUBJECT_W;
  1759. WCHAR *lpszColon = wcschr(lpsPropValue->Value.lpszW, ':');
  1760. if (lpszColon && (lpszColon - lpsPropValue->Value.lpszW) > 1 && (lpszColon - lpsPropValue->Value.lpszW) < 4) {
  1761. WCHAR *c = lpsPropValue->Value.lpszW;
  1762. while (c < lpszColon && iswdigit(*c))
  1763. ++c; // test for all digits prefix
  1764. if (c != lpszColon) {
  1765. ++lpszColon;
  1766. if (*lpszColon == ' ')
  1767. ++lpszColon;
  1768. lpsPropValue->Value.lpszW = lpszColon; // set new subject string
  1769. }
  1770. }
  1771. } else {
  1772. lpsPropValue->ulPropTag = PR_NORMALIZED_SUBJECT_A;
  1773. char *lpszColon = strchr(lpsPropValue->Value.lpszA, ':');
  1774. if (lpszColon && (lpszColon - lpsPropValue->Value.lpszA) > 1 && (lpszColon - lpsPropValue->Value.lpszA) < 4) {
  1775. char *c = lpsPropValue->Value.lpszA;
  1776. while (c < lpszColon && isdigit(*c))
  1777. ++c; // test for all digits prefix
  1778. if (c != lpszColon) {
  1779. ++lpszColon;
  1780. if (*lpszColon == ' ')
  1781. ++lpszColon;
  1782. lpsPropValue->Value.lpszA = lpszColon; // set new subject string
  1783. }
  1784. }
  1785. }
  1786. break;
  1787. case PROP_ID(PR_PARENT_ENTRYID):
  1788. if(!lpMessage->m_lpParentID)
  1789. hr = lpMessage->HrGetRealProp(PR_PARENT_ENTRYID, ulFlags, lpBase, lpsPropValue);
  1790. else{
  1791. lpsPropValue->ulPropTag = PR_PARENT_ENTRYID;
  1792. lpsPropValue->Value.bin.cb = lpMessage->m_cbParentID;
  1793. hr = ECAllocateMore(lpsPropValue->Value.bin.cb, lpBase, reinterpret_cast<void **>(&lpsPropValue->Value.bin.lpb));
  1794. if (hr != hrSuccess)
  1795. break;
  1796. memcpy(lpsPropValue->Value.bin.lpb, lpMessage->m_lpParentID, lpsPropValue->Value.bin.cb);
  1797. }
  1798. break;
  1799. case PROP_ID(PR_MESSAGE_SIZE):
  1800. lpsPropValue->ulPropTag = PR_MESSAGE_SIZE;
  1801. if(lpMessage->m_lpEntryId == NULL) //new message
  1802. lpsPropValue->Value.l = 1024;
  1803. else
  1804. hr = lpMessage->HrGetRealProp(PR_MESSAGE_SIZE, ulFlags, lpBase, lpsPropValue);
  1805. break;
  1806. case PROP_ID(PR_DISPLAY_TO):
  1807. case PROP_ID(PR_DISPLAY_CC):
  1808. case PROP_ID(PR_DISPLAY_BCC):
  1809. if((lpMessage->m_bRecipsDirty && lpMessage->SyncRecips() != erSuccess) || lpMessage->HrGetRealProp(ulPropTag, ulFlags, lpBase, lpsPropValue) != erSuccess) {
  1810. lpsPropValue->ulPropTag = ulPropTag;
  1811. if(PROP_TYPE(ulPropTag) == PT_UNICODE)
  1812. lpsPropValue->Value.lpszW = const_cast<wchar_t *>(L"");
  1813. else
  1814. lpsPropValue->Value.lpszA = const_cast<char *>("");
  1815. }
  1816. break;
  1817. case PROP_ID(PR_ACCESS):
  1818. if(lpMessage->HrGetRealProp(PR_ACCESS, ulFlags, lpBase, lpsPropValue) != hrSuccess)
  1819. {
  1820. lpsPropValue->ulPropTag = PR_ACCESS;
  1821. lpsPropValue->Value.l = MAPI_ACCESS_READ | MAPI_ACCESS_MODIFY | MAPI_ACCESS_DELETE;
  1822. }
  1823. break;
  1824. case PROP_ID(PR_MESSAGE_ATTACHMENTS):
  1825. lpsPropValue->ulPropTag = PR_MESSAGE_ATTACHMENTS;
  1826. lpsPropValue->Value.x = 1;
  1827. break;
  1828. case PROP_ID(PR_MESSAGE_RECIPIENTS):
  1829. lpsPropValue->ulPropTag = PR_MESSAGE_RECIPIENTS;
  1830. lpsPropValue->Value.x = 1;
  1831. break;
  1832. #ifdef HAVE_TIDY_H
  1833. case PROP_ID(PR_EC_BODY_FILTERED): {
  1834. // does it already exist? (e.g. inserted by dagent/gateway)
  1835. hr = lpMessage->GetSyncedBodyProp(PR_EC_BODY_FILTERED, ulFlags, lpBase, lpsPropValue);
  1836. if (hr == hrSuccess) // yes, then use that
  1837. break;
  1838. // else generate it on the fly
  1839. memory_ptr<SPropValue> tprop;
  1840. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~tprop);
  1841. if (hr != hrSuccess)
  1842. break;
  1843. hr = lpMessage->GetSyncedBodyProp(PR_HTML, ulFlags, tprop, tprop);
  1844. if (hr != hrSuccess) {
  1845. hr = MAPI_E_NOT_FOUND;
  1846. break;
  1847. }
  1848. std::string fltblk, memblk(reinterpret_cast<const char *>(tprop->Value.bin.lpb), tprop->Value.bin.cb);
  1849. std::copy_if(fltblk.cbegin(), fltblk.cend(), std::back_inserter(fltblk), [](char x) { return x != '\0'; });
  1850. std::string result;
  1851. std::vector<std::string> errors;
  1852. bool rc = rosie_clean_html(fltblk, &result, &errors);
  1853. // FIXME emit error somewhere somehow
  1854. if (rc) {
  1855. ULONG ulSize = result.size();
  1856. hr = ECAllocateMore(ulSize + 1, lpBase, reinterpret_cast<void **>(&lpsPropValue->Value.lpszA));
  1857. if (hr == hrSuccess) {
  1858. memcpy(lpsPropValue->Value.lpszA, result.c_str(), ulSize);
  1859. lpsPropValue->Value.lpszA[ulSize] = '\0';
  1860. // FIXME store in database if that is what the SysOp wants
  1861. } else {
  1862. ulSize = 0;
  1863. }
  1864. lpsPropValue->Value.bin.cb = ulSize;
  1865. }
  1866. if (rc == 0 || hr != hrSuccess) {
  1867. hr = MAPI_E_NOT_FOUND;
  1868. break;
  1869. }
  1870. break;
  1871. }
  1872. #endif
  1873. case PROP_ID(PR_BODY):
  1874. case PROP_ID(PR_RTF_COMPRESSED):
  1875. case PROP_ID(PR_HTML):
  1876. hr = lpMessage->GetSyncedBodyProp(ulPropTag, ulFlags, lpBase, lpsPropValue);
  1877. if (hr != hrSuccess)
  1878. break;
  1879. if (ulPropTag == PR_BODY_HTML) {
  1880. // Workaround for support html in outlook 2000/xp
  1881. if (lpsPropValue->ulPropTag != PR_HTML){
  1882. hr = MAPI_E_NOT_FOUND;
  1883. break;
  1884. }
  1885. lpsPropValue->ulPropTag = PR_BODY_HTML;
  1886. ulSize = lpsPropValue->Value.bin.cb;
  1887. lpData = lpsPropValue->Value.bin.lpb;
  1888. hr = ECAllocateMore(ulSize + 1, lpBase, (void**)&lpsPropValue->Value.lpszA);
  1889. if(hr != hrSuccess)
  1890. break;
  1891. if(ulSize>0 && lpData){
  1892. memcpy(lpsPropValue->Value.lpszA, lpData, ulSize);
  1893. }else
  1894. ulSize = 0;
  1895. lpsPropValue->Value.lpszA[ulSize] = 0;
  1896. }
  1897. break;
  1898. case PROP_ID(PR_SOURCE_KEY): {
  1899. std::string strServerGUID;
  1900. std::string strID;
  1901. std::string strSourceKey;
  1902. if(ECMAPIProp::DefaultMAPIGetProp(PR_SOURCE_KEY, lpProvider, ulFlags, lpsPropValue, lpParam, lpBase) == hrSuccess)
  1903. return hr;
  1904. // The server did not supply a PR_SOURCE_KEY, generate one ourselves.
  1905. strServerGUID.assign((char*)&lpMessage->GetMsgStore()->GetStoreGuid(), sizeof(GUID));
  1906. if(lpMessage->m_sMapiObject)
  1907. strID.assign((char *)&lpMessage->m_sMapiObject->ulObjId, sizeof(lpMessage->m_sMapiObject->ulObjId));
  1908. // Resize so it trails 6 null bytes
  1909. strID.resize(6,0);
  1910. strSourceKey = strServerGUID + strID;
  1911. hr = MAPIAllocateMore(strSourceKey.size(), lpBase, (void **)&lpsPropValue->Value.bin.lpb);
  1912. if(hr != hrSuccess)
  1913. return hr;
  1914. lpsPropValue->ulPropTag = PR_SOURCE_KEY;
  1915. lpsPropValue->Value.bin.cb = strSourceKey.size();
  1916. memcpy(lpsPropValue->Value.bin.lpb, strSourceKey.c_str(), strSourceKey.size());
  1917. break;
  1918. }
  1919. default:
  1920. hr = MAPI_E_NOT_FOUND;
  1921. break;
  1922. }
  1923. return hr;
  1924. }
  1925. HRESULT ECMessage::SetPropHandler(ULONG ulPropTag, void *lpProvider,
  1926. const SPropValue *lpsPropValue, void *lpParam)
  1927. {
  1928. auto lpMessage = static_cast<ECMessage *>(lpParam);
  1929. HRESULT hr = hrSuccess;
  1930. switch(ulPropTag) {
  1931. case PR_MESSAGE_SIZE:
  1932. /*
  1933. * Accept manipulation of the message size is only accepted
  1934. * while the message has not yet been saved. When parsing the
  1935. * RFC2822 message, SetProp(PR_MESSAGE_SIZE) is called since
  1936. * the message size needs to be known during rules processing.
  1937. * Whenever the message is saved, the size will be recomputed
  1938. * (including properties, etc.), therefore modifications will
  1939. * not be accepted.
  1940. */
  1941. if (lpMessage->fNew)
  1942. hr = lpMessage->HrSetRealProp(lpsPropValue);
  1943. else
  1944. hr = MAPI_E_COMPUTED;
  1945. break;
  1946. case PR_HTML:
  1947. hr = lpMessage->HrSetRealProp(lpsPropValue);
  1948. break;
  1949. case PR_BODY_HTML: {
  1950. // Set PR_BODY_HTML to PR_HTML
  1951. SPropValue copy;
  1952. copy.ulPropTag = PR_HTML;
  1953. auto lpData = copy.Value.lpszA;
  1954. if(lpData) {
  1955. copy.Value.bin.cb = strlen(lpData);
  1956. copy.Value.bin.lpb = (LPBYTE)lpData;
  1957. }
  1958. else {
  1959. copy.Value.bin.cb = 0;
  1960. }
  1961. hr = lpMessage->HrSetRealProp(&copy);
  1962. break;
  1963. }
  1964. case PR_MESSAGE_FLAGS:
  1965. if (lpMessage->m_sMapiObject == NULL || lpMessage->m_sMapiObject->ulObjId == 0) {
  1966. // filter any invalid flags
  1967. SPropValue copy = *lpsPropValue;
  1968. copy.Value.l &= 0x03FF;
  1969. if (lpMessage->HasAttachment())
  1970. copy.Value.l |= MSGFLAG_HASATTACH;
  1971. hr = lpMessage->HrSetRealProp(&copy);
  1972. }
  1973. break;
  1974. case PR_SOURCE_KEY:
  1975. hr = ECMAPIProp::SetPropHandler(ulPropTag, lpProvider, lpsPropValue, lpParam);
  1976. break;
  1977. default:
  1978. hr = MAPI_E_NOT_FOUND;
  1979. break;
  1980. }
  1981. return hr;
  1982. }
  1983. // Use the support object to do the copying
  1984. HRESULT ECMessage::CopyTo(ULONG ciidExclude, LPCIID rgiidExclude,
  1985. const SPropTagArray *lpExcludeProps, ULONG ulUIParam,
  1986. LPMAPIPROGRESS lpProgress, LPCIID lpInterface, void *lpDestObj,
  1987. ULONG ulFlags, SPropProblemArray **lppProblems)
  1988. {
  1989. HRESULT hr = hrSuccess;
  1990. object_ptr<IECUnknown> lpECUnknown;
  1991. memory_ptr<SPropValue> lpECObject;
  1992. object_ptr<ECMAPIProp> lpECMAPIProp;
  1993. ECMAPIProp *lpDestTop = NULL;
  1994. ECMAPIProp *lpSourceTop = NULL;
  1995. GUID sDestServerGuid = {0};
  1996. GUID sSourceServerGuid = {0};
  1997. if (lpDestObj == nullptr)
  1998. return MAPI_E_INVALID_PARAMETER;
  1999. // Wrap mapi object to kopano object
  2000. if (HrGetOneProp((LPMAPIPROP)lpDestObj, PR_EC_OBJECT, &~lpECObject) == hrSuccess)
  2001. lpECUnknown.reset(reinterpret_cast<IECUnknown *>(lpECObject->Value.lpszA));
  2002. // Deny copying within the same object. This is not allowed in exchange either and is required to deny
  2003. // creating large recursive objects.
  2004. if(lpECUnknown && lpECUnknown->QueryInterface(IID_ECMAPIProp, &~lpECMAPIProp) == hrSuccess) {
  2005. // Find the top-level objects for both source and destination objects
  2006. lpDestTop = lpECMAPIProp->m_lpRoot;
  2007. lpSourceTop = this->m_lpRoot;
  2008. // destination may not be a child of the source, but source can be a child of destination
  2009. if (!this->IsChildOf(lpDestTop)) {
  2010. // ICS expects the entryids to be equal. So check if the objects reside on
  2011. // the same server as well.
  2012. hr = lpDestTop->GetMsgStore()->lpTransport->GetServerGUID(&sDestServerGuid);
  2013. if (hr != hrSuccess)
  2014. return hr;
  2015. hr = lpSourceTop->GetMsgStore()->lpTransport->GetServerGUID(&sSourceServerGuid);
  2016. if (hr != hrSuccess)
  2017. return hr;
  2018. if(lpDestTop->m_lpEntryId && lpSourceTop->m_lpEntryId &&
  2019. lpDestTop->m_cbEntryId == lpSourceTop->m_cbEntryId &&
  2020. memcmp(lpDestTop->m_lpEntryId, lpSourceTop->m_lpEntryId, lpDestTop->m_cbEntryId) == 0 &&
  2021. sDestServerGuid == sSourceServerGuid)
  2022. // Source and destination are the same on-disk objects (entryids are equal)
  2023. return MAPI_E_NO_ACCESS;
  2024. }
  2025. }
  2026. return Util::DoCopyTo(&IID_IMessage, &this->m_xMessage, ciidExclude,
  2027. rgiidExclude, lpExcludeProps, ulUIParam, lpProgress,
  2028. lpInterface, lpDestObj, ulFlags, lppProblems);
  2029. }
  2030. // We override HrLoadProps to setup PR_BODY and PR_RTF_COMPRESSED in the initial message
  2031. // Normally, this should never be needed, as messages should store both the PR_BODY as the PR_RTF_COMPRESSED
  2032. // when saving.
  2033. HRESULT ECMessage::HrLoadProps()
  2034. {
  2035. HRESULT hr = hrSuccess;
  2036. ecmem_ptr<SPropValue> lpsBodyProps;
  2037. static constexpr const SizedSPropTagArray(3, sPropBodyTags) =
  2038. {3, {PR_BODY_W, PR_RTF_COMPRESSED, PR_HTML}};
  2039. ULONG cValues = 0;
  2040. BOOL fBodyOK = FALSE;
  2041. BOOL fRTFOK = FALSE;
  2042. BOOL fHTMLOK = FALSE;
  2043. m_bInhibitSync = TRUE; // We don't want the logic in ECMessage::HrSetRealProp to kick in yet.
  2044. hr = ECMAPIProp::HrLoadProps();
  2045. m_bInhibitSync = FALSE;
  2046. if (hr != hrSuccess)
  2047. return hr;
  2048. /*
  2049. * Now we're going to determine what the best body is.
  2050. * This works as follows, the db will always contain the best body, but possibly
  2051. * more. The plaintext body should also always be available.
  2052. *
  2053. * So if we only get a plaintext body, plaintext was the best body.
  2054. * If we get HTML but not RTF, HTML is the best body.
  2055. * If we get RTF, we'll check the RTF content to determine what the best body was.
  2056. *
  2057. * We won't generate any body except the best body if it wasn't returned by the
  2058. * server, which is actually wrong.
  2059. */
  2060. hr = ECMAPIProp::GetProps(sPropBodyTags, 0, &cValues, &~lpsBodyProps);
  2061. if (HR_FAILED(hr))
  2062. return hr;
  2063. hr = hrSuccess;
  2064. if (lpsBodyProps[0].ulPropTag == PR_BODY_W || (lpsBodyProps[0].ulPropTag == PROP_TAG(PT_ERROR, PROP_ID(PR_BODY)) && lpsBodyProps[0].Value.err == MAPI_E_NOT_ENOUGH_MEMORY))
  2065. fBodyOK = TRUE;
  2066. if (lpsBodyProps[1].ulPropTag == PR_RTF_COMPRESSED || (lpsBodyProps[1].ulPropTag == PROP_TAG(PT_ERROR, PROP_ID(PR_RTF_COMPRESSED)) && lpsBodyProps[1].Value.err == MAPI_E_NOT_ENOUGH_MEMORY))
  2067. fRTFOK = TRUE;
  2068. if (lpsBodyProps[2].ulPropTag == PR_HTML || (lpsBodyProps[2].ulPropTag == PROP_TAG(PT_ERROR, PROP_ID(PR_HTML)) && lpsBodyProps[2].Value.err == MAPI_E_NOT_ENOUGH_MEMORY))
  2069. fHTMLOK = TRUE;
  2070. if (fRTFOK) {
  2071. HRESULT hrTmp = hrSuccess;
  2072. hrTmp = GetBodyType(&m_ulBodyType);
  2073. if (FAILED(hrTmp)) {
  2074. // eg. this fails then RTF property is present but empty
  2075. TRACE_MAPI(TRACE_WARNING, "GetBestBody", "Unable to determine body type based on RTF data, hr=0x%08x", hrTmp);
  2076. } else if ((m_ulBodyType == bodyTypePlain && !fBodyOK) ||
  2077. (m_ulBodyType == bodyTypeHTML && !fHTMLOK)) {
  2078. hr = SyncRtf();
  2079. if (hr != hrSuccess)
  2080. return hr;
  2081. }
  2082. }
  2083. if (m_ulBodyType == bodyTypeUnknown) {
  2084. // We get here if there was no RTF data or when determining the body type based
  2085. // on that data failed.
  2086. if (fHTMLOK)
  2087. m_ulBodyType = bodyTypeHTML;
  2088. else if (fBodyOK)
  2089. m_ulBodyType = bodyTypePlain;
  2090. }
  2091. return hr;
  2092. }
  2093. HRESULT ECMessage::HrSetRealProp(const SPropValue *lpsPropValue)
  2094. {
  2095. HRESULT hr;
  2096. hr = ECMAPIProp::HrSetRealProp(lpsPropValue);
  2097. if(hr != hrSuccess)
  2098. return hr;
  2099. // If we're in the middle of syncing bodies, we don't want any more logic to kick in.
  2100. if (m_bInhibitSync)
  2101. return hrSuccess;
  2102. if (lpsPropValue->ulPropTag == PR_RTF_COMPRESSED) {
  2103. m_ulBodyType = bodyTypeUnknown; // Make sure GetBodyType doesn't use the cached value
  2104. GetBodyType(&m_ulBodyType);
  2105. SyncRtf();
  2106. } else if (lpsPropValue->ulPropTag == PR_HTML) {
  2107. m_ulBodyType = bodyTypeHTML;
  2108. SyncHtmlToPlain();
  2109. HrDeleteRealProp(PR_RTF_COMPRESSED, FALSE);
  2110. } else if (lpsPropValue->ulPropTag == PR_BODY_W || lpsPropValue->ulPropTag == PR_BODY_A) {
  2111. m_ulBodyType = bodyTypePlain;
  2112. HrDeleteRealProp(PR_RTF_COMPRESSED, FALSE);
  2113. HrDeleteRealProp(PR_HTML, FALSE);
  2114. }
  2115. return hrSuccess;
  2116. }
  2117. struct findobject_if {
  2118. unsigned int m_ulUniqueId;
  2119. unsigned int m_ulObjType;
  2120. findobject_if(unsigned int ulObjType, unsigned int ulUniqueId) : m_ulUniqueId(ulUniqueId), m_ulObjType(ulObjType) {}
  2121. bool operator()(const MAPIOBJECT *entry)
  2122. {
  2123. return entry->ulUniqueId == m_ulUniqueId && entry->ulObjType == m_ulObjType;
  2124. }
  2125. };
  2126. // Copies the server object IDs from lpSrc into lpDest by matching the correct object type
  2127. // and unique ID for each object.
  2128. static HRESULT HrCopyObjIDs(MAPIOBJECT *lpDest, const MAPIOBJECT *lpSrc)
  2129. {
  2130. HRESULT hr;
  2131. lpDest->ulObjId = lpSrc->ulObjId;
  2132. for (const auto &src : lpSrc->lstChildren) {
  2133. auto iterDest = lpDest->lstChildren.find(src);
  2134. if (iterDest != lpDest->lstChildren.cend()) {
  2135. hr = HrCopyObjIDs(*iterDest, src);
  2136. if(hr != hrSuccess)
  2137. return hr;
  2138. }
  2139. }
  2140. return hrSuccess;
  2141. }
  2142. HRESULT ECMessage::HrSaveChild(ULONG ulFlags, MAPIOBJECT *lpsMapiObject) {
  2143. HRESULT hr = hrSuccess;
  2144. ECMapiObjects::const_iterator iterSObj;
  2145. SPropValue sKeyProp;
  2146. ecmem_ptr<SPropValue> lpProps;
  2147. ULONG ulProps = 0;
  2148. LPSPropValue lpPropID = NULL;
  2149. LPSPropValue lpPropObjType = NULL;
  2150. ULONG i;
  2151. scoped_rlock lock(m_hMutexMAPIObject);
  2152. if (lpsMapiObject->ulObjType != MAPI_ATTACH)
  2153. // can only save attachments as child objects
  2154. // (recipients are saved through SaveRecips() from SaveChanges() on this object)
  2155. return MAPI_E_INVALID_OBJECT;
  2156. if(this->lpAttachments == NULL) {
  2157. object_ptr<IMAPITable> lpTable;
  2158. hr = this->GetAttachmentTable(fMapiUnicode, &~lpTable);
  2159. if(hr != hrSuccess)
  2160. return hr;
  2161. }
  2162. if (this->lpAttachments == nullptr)
  2163. return MAPI_E_CALL_FAILED;
  2164. if (!m_sMapiObject) {
  2165. // when does this happen? .. just a simple precaution for now
  2166. assert(m_sMapiObject != NULL);
  2167. return MAPI_E_NOT_FOUND;
  2168. }
  2169. // Replace the attachment in the object hierarchy with this one, but preserve server object id. This is needed
  2170. // if the entire object has been saved to the server in the mean time.
  2171. iterSObj = m_sMapiObject->lstChildren.find(lpsMapiObject);
  2172. if (iterSObj != m_sMapiObject->lstChildren.cend()) {
  2173. // Preserve server IDs
  2174. hr = HrCopyObjIDs(lpsMapiObject, (*iterSObj));
  2175. if(hr != hrSuccess)
  2176. return hr;
  2177. // Remove item
  2178. FreeMapiObject(*iterSObj);
  2179. m_sMapiObject->lstChildren.erase(iterSObj);
  2180. }
  2181. m_sMapiObject->lstChildren.insert(new MAPIOBJECT(lpsMapiObject));
  2182. // Update the attachment table. The attachment table contains all properties of the attachments
  2183. ulProps = lpsMapiObject->lstProperties.size();
  2184. // +2 for maybe missing PR_ATTACH_NUM and PR_OBJECT_TYPE properties
  2185. hr = ECAllocateBuffer(sizeof(SPropValue) * (ulProps + 2), &~lpProps);
  2186. if (hr != hrSuccess)
  2187. return hr;
  2188. lpPropID = NULL;
  2189. i = 0;
  2190. for (const auto &pv : lpsMapiObject->lstProperties) {
  2191. pv.CopyToByRef(&lpProps[i]);
  2192. if (lpProps[i].ulPropTag == PR_ATTACH_NUM) {
  2193. lpPropID = &lpProps[i];
  2194. } else if(lpProps[i].ulPropTag == PR_OBJECT_TYPE) {
  2195. lpPropObjType = &lpProps[i];
  2196. } else if (PROP_ID(lpProps[i].ulPropTag) == PROP_ID(PR_ATTACH_DATA_OBJ)) {
  2197. lpProps[i].ulPropTag = CHANGE_PROP_TYPE(lpProps[i].ulPropTag, PT_ERROR);
  2198. lpProps[i].Value.err = MAPI_E_NOT_ENOUGH_MEMORY;
  2199. } else if (PROP_TYPE(lpProps[i].ulPropTag) == PT_BINARY && lpProps[i].Value.bin.cb > MAX_TABLE_PROPSIZE) {
  2200. lpProps[i].ulPropTag = CHANGE_PROP_TYPE(lpProps[i].ulPropTag, PT_ERROR);
  2201. lpProps[i].Value.err = MAPI_E_NOT_ENOUGH_MEMORY;
  2202. }
  2203. ++i;
  2204. }
  2205. if (lpPropID == NULL) {
  2206. ++ulProps;
  2207. lpPropID = &lpProps[i++];
  2208. }
  2209. if (lpPropObjType == NULL) {
  2210. ++ulProps;
  2211. lpPropObjType = &lpProps[i++];
  2212. }
  2213. lpPropObjType->ulPropTag = PR_OBJECT_TYPE;
  2214. lpPropObjType->Value.ul = MAPI_ATTACH;
  2215. lpPropID->ulPropTag = PR_ATTACH_NUM;
  2216. lpPropID->Value.ul = lpsMapiObject->ulUniqueId;
  2217. sKeyProp.ulPropTag = PR_EC_HIERARCHYID;
  2218. sKeyProp.Value.ul = lpsMapiObject->ulObjId;
  2219. return lpAttachments->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, &sKeyProp, lpProps, ulProps);
  2220. }
  2221. HRESULT ECMessage::GetBodyType(eBodyType *lpulBodyType)
  2222. {
  2223. HRESULT hr = hrSuccess;
  2224. object_ptr<IStream> lpRTFCompressedStream, lpRTFUncompressedStream;
  2225. char szRtfBuf[64] = {0};
  2226. ULONG cbRtfBuf = 0;
  2227. if (m_ulBodyType == bodyTypeUnknown) {
  2228. hr = OpenProperty(PR_RTF_COMPRESSED, &IID_IStream, 0, 0, &~lpRTFCompressedStream);
  2229. if (hr != hrSuccess)
  2230. return hr;
  2231. hr = WrapCompressedRTFStream(lpRTFCompressedStream, 0, &~lpRTFUncompressedStream);
  2232. if (hr != hrSuccess)
  2233. return hr;
  2234. hr = lpRTFUncompressedStream->Read(szRtfBuf, sizeof(szRtfBuf), &cbRtfBuf);
  2235. if (hr != hrSuccess)
  2236. return hr;
  2237. if (isrtftext(szRtfBuf, cbRtfBuf))
  2238. m_ulBodyType = bodyTypePlain;
  2239. else if (isrtfhtml(szRtfBuf, cbRtfBuf))
  2240. m_ulBodyType = bodyTypeHTML;
  2241. else
  2242. m_ulBodyType = bodyTypeRTF;
  2243. }
  2244. *lpulBodyType = m_ulBodyType;
  2245. return hrSuccess;
  2246. }
  2247. HRESULT ECMessage::GetRtfData(std::string *lpstrRtfData)
  2248. {
  2249. HRESULT hr;
  2250. StreamPtr ptrRtfCompressedStream;
  2251. StreamPtr ptrRtfUncompressedStream;
  2252. char lpBuf[4096];
  2253. std::string strRtfData;
  2254. hr = OpenProperty(PR_RTF_COMPRESSED, &IID_IStream, 0, 0, &~ptrRtfCompressedStream);
  2255. if (hr != hrSuccess)
  2256. return hr;
  2257. // Read the RTF stream
  2258. hr = WrapCompressedRTFStream(ptrRtfCompressedStream, 0, &~ptrRtfUncompressedStream);
  2259. if(hr != hrSuccess)
  2260. {
  2261. KCHL::object_ptr<ECMemStream> ptrEmptyMemStream;
  2262. // Broken RTF, fallback on empty stream
  2263. hr = ECMemStream::Create(nullptr, 0, 0, nullptr, nullptr, nullptr, &~ptrEmptyMemStream);
  2264. if (hr != hrSuccess)
  2265. return hr;
  2266. hr = ptrEmptyMemStream->QueryInterface(IID_IStream, &~ptrRtfUncompressedStream);
  2267. if (hr != hrSuccess)
  2268. return hr;
  2269. }
  2270. // Read the entire uncompressed RTF stream into strRTF
  2271. while (1) {
  2272. ULONG ulRead;
  2273. hr = ptrRtfUncompressedStream->Read(lpBuf, 4096, &ulRead);
  2274. if (hr != hrSuccess)
  2275. return hr;
  2276. if (ulRead == 0)
  2277. break;
  2278. strRtfData.append(lpBuf, ulRead);
  2279. }
  2280. lpstrRtfData->swap(strRtfData);
  2281. return hrSuccess;
  2282. }
  2283. HRESULT ECMessage::GetCodePage(unsigned int *lpulCodePage)
  2284. {
  2285. HRESULT hr;
  2286. SPropValuePtr ptrPropValue;
  2287. hr = ECAllocateBuffer(sizeof(SPropValue), &~ptrPropValue);
  2288. if (hr != hrSuccess)
  2289. return hr;
  2290. if (HrGetRealProp(PR_INTERNET_CPID, 0, ptrPropValue, ptrPropValue) == hrSuccess &&
  2291. ptrPropValue->ulPropTag == PR_INTERNET_CPID)
  2292. *lpulCodePage = ptrPropValue->Value.ul;
  2293. else
  2294. *lpulCodePage = 0;
  2295. return hrSuccess;
  2296. }
  2297. // Use the support object to do the copying
  2298. HRESULT ECMessage::CopyProps(const SPropTagArray *lpIncludeProps,
  2299. ULONG ulUIParam, LPMAPIPROGRESS lpProgress, LPCIID lpInterface,
  2300. void *lpDestObj, ULONG ulFlags, SPropProblemArray **lppProblems)
  2301. {
  2302. return Util::DoCopyProps(&IID_IMessage, &this->m_xMessage, lpIncludeProps, ulUIParam, lpProgress, lpInterface, lpDestObj, ulFlags, lppProblems);
  2303. }
  2304. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, QueryInterface, (REFIID, refiid), (void **, lppInterface))
  2305. DEF_ULONGMETHOD1(TRACE_MAPI, ECMessage, Message, AddRef, (void))
  2306. DEF_ULONGMETHOD1(TRACE_MAPI, ECMessage, Message, Release, (void))
  2307. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, GetLastError, (HRESULT, hError), (ULONG, ulFlags), (LPMAPIERROR *, lppMapiError))
  2308. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, SaveChanges, (ULONG, ulFlags))
  2309. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, GetProps, (const SPropTagArray *, lpPropTagArray), (ULONG, ulFlags), (ULONG *, lpcValues), (SPropValue **, lppPropArray))
  2310. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, GetPropList, (ULONG, ulFlags), (LPSPropTagArray *, lppPropTagArray))
  2311. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, OpenProperty, (ULONG, ulPropTag), (LPCIID, lpiid), (ULONG, ulInterfaceOptions), (ULONG, ulFlags), (LPUNKNOWN *, lppUnk))
  2312. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, SetProps, (ULONG, cValues), (const SPropValue *, lpPropArray), (SPropProblemArray **, lppProblems))
  2313. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, DeleteProps, (const SPropTagArray *, lpPropTagArray), (SPropProblemArray **, lppProblems))
  2314. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, CopyTo, (ULONG, ciidExclude), (LPCIID, rgiidExclude), (const SPropTagArray *, lpExcludeProps), (ULONG, ulUIParam), (LPMAPIPROGRESS, lpProgress), (LPCIID, lpInterface), (void *, lpDestObj), (ULONG, ulFlags), (SPropProblemArray **, lppProblems))
  2315. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, CopyProps, (const SPropTagArray *, lpIncludeProps), (ULONG, ulUIParam), (LPMAPIPROGRESS, lpProgress), (LPCIID, lpInterface), (void *, lpDestObj), (ULONG, ulFlags), (SPropProblemArray **, lppProblems))
  2316. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, GetNamesFromIDs, (LPSPropTagArray *, pptaga), (LPGUID, lpguid), (ULONG, ulFlags), (ULONG *, pcNames), (LPMAPINAMEID **, pppNames))
  2317. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, GetIDsFromNames, (ULONG, cNames), (LPMAPINAMEID *, ppNames), (ULONG, ulFlags), (LPSPropTagArray *, pptaga))
  2318. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, GetAttachmentTable, (ULONG, ulFlags), (LPMAPITABLE *, lppTable))
  2319. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, OpenAttach, (ULONG, ulAttachmentNum), (LPCIID, lpInterface), (ULONG, ulFlags), (LPATTACH *, lppAttach))
  2320. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, CreateAttach, (LPCIID, lpInterface), (ULONG, ulFlags), (ULONG *, lpulAttachmentNum), (LPATTACH *, lppAttach))
  2321. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, DeleteAttach, (ULONG, ulAttachmentNum), (ULONG, ulUIParam), (LPMAPIPROGRESS, lpProgress), (ULONG, ulFlags))
  2322. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, GetRecipientTable, (ULONG, ulFlags), (LPMAPITABLE *, lppTable))
  2323. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, ModifyRecipients, (ULONG, ulFlags), (const ADRLIST *, lpMods))
  2324. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, SubmitMessage, (ULONG, ulFlags))
  2325. DEF_HRMETHOD1(TRACE_MAPI, ECMessage, Message, SetReadFlag, (ULONG, ulFlags))