Util.cpp 128 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/zcdefs.h>
  18. #include <kopano/platform.h>
  19. #include <exception>
  20. #include <cwctype>
  21. #include <mapidefs.h>
  22. #include <mapiutil.h>
  23. #include <mapispi.h>
  24. #include <memory>
  25. #include <new>
  26. #include <string>
  27. #include <stack>
  28. #include <set>
  29. #include <map>
  30. #include <cstring>
  31. #include <edkmdb.h>
  32. #include <kopano/Util.h>
  33. #include <kopano/ECIConv.h>
  34. #include <kopano/CommonUtil.h>
  35. #include <kopano/memory.hpp>
  36. #include <kopano/stringutil.h>
  37. #include <kopano/charset/convert.h>
  38. #include "ECMemStream.h"
  39. #include <kopano/IECSingleInstance.h>
  40. #include <kopano/ECGuid.h>
  41. #include <kopano/codepage.h>
  42. #include "rtfutil.h"
  43. #include <kopano/mapiext.h>
  44. #include <kopano/ustringutil.h>
  45. #include <kopano/mapi_ptr.h>
  46. #include "HtmlToTextParser.h"
  47. #include <kopano/ECLogger.h>
  48. #include "HtmlEntity.h"
  49. using namespace std;
  50. using namespace KCHL;
  51. #include <kopano/ECGetText.h>
  52. namespace KC {
  53. // HACK: prototypes may differ depending on the compiler and/or system (the
  54. // second parameter may or may not be 'const'). This redeclaration is a hack
  55. // to have a common prototype "iconv_cast".
  56. class iconv_HACK _kc_final {
  57. public:
  58. iconv_HACK(const char** ptr) : m_ptr(ptr) { }
  59. // the compiler will choose the right operator
  60. operator const char **(void) const { return m_ptr; }
  61. operator char**() { return const_cast <char**>(m_ptr); }
  62. private:
  63. const char** m_ptr;
  64. };
  65. class PropTagCompare _kc_final {
  66. public:
  67. bool operator()(ULONG lhs, ULONG rhs) const {
  68. if (PROP_TYPE(lhs) == PT_UNSPECIFIED || PROP_TYPE(rhs) == PT_UNSPECIFIED)
  69. return PROP_ID(lhs) < PROP_ID(rhs);
  70. return lhs < rhs;
  71. }
  72. };
  73. typedef std::set<ULONG,PropTagCompare> PropTagSet;
  74. /**
  75. * Add or replaces a prop value in a an SPropValue array
  76. *
  77. * @param[in] lpSrc Array of properties
  78. * @param[in] cValues Number of properties in lpSrc
  79. * @param[in] lpToAdd Add or replace this property
  80. * @param[out] lppDest All properties returned
  81. * @param[out] cDestValues Number of properties in lppDest
  82. *
  83. * @return MAPI error code
  84. */
  85. HRESULT Util::HrAddToPropertyArray(const SPropValue *lpSrc, ULONG cValues,
  86. const SPropValue *lpToAdd, SPropValue **lppDest, ULONG *cDestValues)
  87. {
  88. LPSPropValue lpDest = NULL;
  89. unsigned int i = 0;
  90. unsigned int n = 0;
  91. HRESULT hr = MAPIAllocateBuffer(sizeof(SPropValue) * (cValues + 1),
  92. reinterpret_cast<void **>(&lpDest));
  93. if (hr != hrSuccess)
  94. return hr;
  95. for (i = 0; i < cValues; ++i) {
  96. hr = HrCopyProperty(&lpDest[n], &lpSrc[i], lpDest);
  97. if(hr == hrSuccess)
  98. ++n;
  99. hr = hrSuccess;
  100. }
  101. auto lpFind = PpropFindProp(lpDest, n, lpToAdd->ulPropTag);
  102. if (lpFind != nullptr)
  103. hr = HrCopyProperty(lpFind, lpToAdd, lpDest);
  104. else
  105. hr = HrCopyProperty(&lpDest[n++], lpToAdd, lpDest);
  106. if(hr != hrSuccess)
  107. return hr;
  108. *lppDest = lpDest;
  109. *cDestValues = n;
  110. return hrSuccess;
  111. }
  112. /**
  113. * Check if object supports HTML
  114. *
  115. * @param lpMapiProp Interface to check
  116. * @result TRUE if object supports unicode, FALSE if not or error
  117. */
  118. #ifndef STORE_HTML_OK
  119. #define STORE_HTML_OK 0x00010000
  120. #endif
  121. bool Util::FHasHTML(IMAPIProp *lpProp)
  122. {
  123. HRESULT hr = hrSuccess;
  124. memory_ptr<SPropValue> lpPropSupport = NULL;
  125. hr = HrGetOneProp(lpProp, PR_STORE_SUPPORT_MASK, &~lpPropSupport);
  126. if(hr != hrSuccess)
  127. return false; /* hr */
  128. if((lpPropSupport->Value.ul & STORE_HTML_OK) == 0)
  129. return false; /* MAPI_E_NOT_FOUND */
  130. return true;
  131. }
  132. /**
  133. * Merges to proptag arrays, lpAdds properties may overwrite properties from lpSrc
  134. *
  135. * @param[in] lpSrc Source property array
  136. * @param[in] cValues Number of properties in lpSrc
  137. * @param[in] lpAdds Properties to combine with lpSrc, adding or replacing values
  138. * @param[in] cAddValues Number of properties in lpAdds
  139. * @param[out] lppDest New array containing all properties
  140. * @param[out] cDestValues Number of properties in lppDest
  141. *
  142. * @return MAPI error code
  143. */
  144. HRESULT Util::HrMergePropertyArrays(const SPropValue *lpSrc, ULONG cValues,
  145. const SPropValue *lpAdds, ULONG cAddValues, SPropValue **lppDest,
  146. ULONG *cDestValues)
  147. {
  148. HRESULT hr = hrSuccess;
  149. map<ULONG, const SPropValue *> mapPropSource;
  150. ULONG i = 0;
  151. memory_ptr<SPropValue> lpProps;
  152. for (i = 0; i < cValues; ++i)
  153. mapPropSource[lpSrc[i].ulPropTag] = &lpSrc[i];
  154. for (i = 0; i < cAddValues; ++i)
  155. mapPropSource[lpAdds[i].ulPropTag] = &lpAdds[i];
  156. hr = MAPIAllocateBuffer(sizeof(SPropValue)*mapPropSource.size(), &~lpProps);
  157. if (hr != hrSuccess)
  158. return hr;
  159. i = 0;
  160. for (const auto &ips : mapPropSource) {
  161. hr = Util::HrCopyProperty(&lpProps[i], ips.second, lpProps);
  162. if (hr != hrSuccess)
  163. return hr;
  164. ++i;
  165. }
  166. *cDestValues = i;
  167. *lppDest = lpProps.release();
  168. return hrSuccess;
  169. }
  170. /**
  171. * Copies a whole array of properties, but leaves the external data
  172. * where it is (ie binary, string data is not copied). PT_ERROR
  173. * properties can be filtered.
  174. *
  175. * @param[in] lpSrc Array of properties
  176. * @param[in] cValues Number of values in lpSrc
  177. * @param[out] lppDest Duplicate array with data pointers into lpSrc values
  178. * @param[out] cDestValues Number of values in lppDest
  179. * @param[in] bExcludeErrors if true, copy even PT_ERROR properties
  180. *
  181. * @return MAPI error code
  182. */
  183. HRESULT Util::HrCopyPropertyArrayByRef(const SPropValue *lpSrc, ULONG cValues,
  184. LPSPropValue *lppDest, ULONG *cDestValues, bool bExcludeErrors)
  185. {
  186. memory_ptr<SPropValue> lpDest;
  187. unsigned int i = 0;
  188. unsigned int n = 0;
  189. HRESULT hr = MAPIAllocateBuffer(sizeof(SPropValue) * cValues, &~lpDest);
  190. if (hr != hrSuccess)
  191. return hr;
  192. for (i = 0; i < cValues; ++i) {
  193. if (bExcludeErrors && PROP_TYPE(lpSrc[i].ulPropTag) == PT_ERROR)
  194. continue;
  195. hr = HrCopyPropertyByRef(&lpDest[n], &lpSrc[i]);
  196. if (hr == hrSuccess)
  197. ++n;
  198. }
  199. *lppDest = lpDest.release();
  200. *cDestValues = n;
  201. return hrSuccess;
  202. }
  203. /**
  204. * Copies a whole array of properties, data of lpSrc will also be
  205. * copied into lppDest. PT_ERROR properties can be filtered.
  206. *
  207. * @param[in] lpSrc Array of properties
  208. * @param[in] cValues Number of values in lpSrc
  209. * @param[out] lppDest Duplicate array with data pointers into lpSrc values
  210. * @param[out] cDestValues Number of values in lppDest
  211. * @param[in] bExcludeErrors if true, copy even PT_ERROR properties
  212. *
  213. * @return MAPI error code
  214. */
  215. HRESULT Util::HrCopyPropertyArray(const SPropValue *lpSrc, ULONG cValues,
  216. LPSPropValue *lppDest, ULONG *cDestValues, bool bExcludeErrors)
  217. {
  218. memory_ptr<SPropValue> lpDest;
  219. unsigned int i = 0;
  220. unsigned int n = 0;
  221. HRESULT hr = MAPIAllocateBuffer(sizeof(SPropValue) * cValues, &~lpDest);
  222. if (hr != hrSuccess)
  223. return hr;
  224. for (i = 0; i < cValues; ++i) {
  225. if (bExcludeErrors && PROP_TYPE(lpSrc[i].ulPropTag) == PT_ERROR)
  226. continue;
  227. hr = HrCopyProperty(&lpDest[n], &lpSrc[i], lpDest);
  228. if (hr == MAPI_E_INVALID_PARAMETER)
  229. /* traditionally ignored */
  230. continue;
  231. else if (hr != hrSuccess)
  232. return hr; /* give back memory errors */
  233. ++n;
  234. }
  235. *lppDest = lpDest.release();
  236. *cDestValues = n;
  237. return hrSuccess;
  238. }
  239. /**
  240. * Copy array of properties in already allocated location.
  241. *
  242. * @param[in] lpSrc Array of properties to copy
  243. * @param[in] cValues Number of properties in lpSrc
  244. * @param[out] lpDest Array of properties with enough space to contain cValues properties
  245. * @param[in] lpBase Base pointer to use with MAPIAllocateMore
  246. *
  247. * @return MAPI error code
  248. */
  249. HRESULT Util::HrCopyPropertyArray(const SPropValue *lpSrc, ULONG cValues,
  250. LPSPropValue lpDest, void *lpBase)
  251. {
  252. unsigned int i;
  253. for (i = 0; i < cValues; ++i) {
  254. HRESULT hr = HrCopyProperty(&lpDest[i], &lpSrc[i], lpBase);
  255. if(hr != hrSuccess)
  256. return hr;
  257. }
  258. return hrSuccess;
  259. }
  260. /**
  261. * Copy array of properties in already allocated location. but leaves the external data
  262. * where it is (ie binary, string data is not copied)
  263. *
  264. * @param[in] lpSrc Array of properties to copy
  265. * @param[in] cValues Number of properties in lpSrc
  266. * @param[out] lpDest Array of properties with enough space to contain cValues properties
  267. * @param[in] lpBase Base pointer to use with MAPIAllocateMore
  268. *
  269. * @return MAPI error code
  270. */
  271. HRESULT Util::HrCopyPropertyArrayByRef(const SPropValue *lpSrc, ULONG cValues,
  272. LPSPropValue lpDest)
  273. {
  274. unsigned int i;
  275. for (i = 0; i < cValues; ++i) {
  276. HRESULT hr = HrCopyPropertyByRef(&lpDest[i], &lpSrc[i]);
  277. if(hr != hrSuccess)
  278. return hr;
  279. }
  280. return hrSuccess;
  281. }
  282. /**
  283. * Copies one property to somewhere else, but doesn't copy external data (ie binary or string data)
  284. *
  285. * @param[out] lpDest Destination to copy property to
  286. * @param[in] lpSrc Source property to make copy of
  287. *
  288. * @return always hrSuccess
  289. */
  290. HRESULT Util::HrCopyPropertyByRef(LPSPropValue lpDest, const SPropValue *lpSrc)
  291. {
  292. // Just a simple memcpy !
  293. memcpy(lpDest, lpSrc, sizeof(SPropValue));
  294. return hrSuccess;
  295. }
  296. /**
  297. * Copies one property to somewhere else, alloc'ing space if required
  298. *
  299. * @param[out] lpDest Destination to copy property to
  300. * @param[in] lpSrc Source property to make copy of
  301. * @param[in] lpBase Base pointer to use with lpfAllocMore
  302. * @param[in] lpfAllocMore Pointer to the MAPIAllocateMore function, can be NULL
  303. *
  304. * @return MAPI error code
  305. */
  306. HRESULT Util::HrCopyProperty(LPSPropValue lpDest, const SPropValue *lpSrc,
  307. void *lpBase, ALLOCATEMORE *lpfAllocMore)
  308. {
  309. HRESULT hr = hrSuccess;
  310. if(lpfAllocMore == NULL)
  311. lpfAllocMore = MAPIAllocateMore;
  312. switch(PROP_TYPE(lpSrc->ulPropTag)) {
  313. case PT_I2:
  314. lpDest->Value.i = lpSrc->Value.i;
  315. break;
  316. case PT_LONG:
  317. lpDest->Value.ul = lpSrc->Value.ul;
  318. break;
  319. case PT_BOOLEAN:
  320. lpDest->Value.b = lpSrc->Value.b;
  321. break;
  322. case PT_R4:
  323. lpDest->Value.flt = lpSrc->Value.flt;
  324. break;
  325. case PT_DOUBLE:
  326. lpDest->Value.dbl = lpSrc->Value.dbl;
  327. break;
  328. case PT_APPTIME:
  329. lpDest->Value.at = lpSrc->Value.at;
  330. break;
  331. case PT_CURRENCY:
  332. lpDest->Value.cur = lpSrc->Value.cur;
  333. break;
  334. case PT_SYSTIME:
  335. lpDest->Value.ft = lpSrc->Value.ft;
  336. break;
  337. case PT_I8:
  338. lpDest->Value.li = lpSrc->Value.li;
  339. break;
  340. case PT_UNICODE:
  341. if (lpSrc->Value.lpszW == NULL)
  342. return MAPI_E_INVALID_PARAMETER;
  343. hr = lpfAllocMore(wcslen(lpSrc->Value.lpszW)*sizeof(wchar_t)+sizeof(wchar_t), lpBase, (void**)&lpDest->Value.lpszW);
  344. if (hr != hrSuccess)
  345. return hr;
  346. wcscpy(lpDest->Value.lpszW, lpSrc->Value.lpszW);
  347. break;
  348. case PT_STRING8:
  349. if (lpSrc->Value.lpszA == NULL)
  350. return MAPI_E_INVALID_PARAMETER;
  351. hr = lpfAllocMore(strlen(lpSrc->Value.lpszA) + 1, lpBase, (void**)&lpDest->Value.lpszA);
  352. if (hr != hrSuccess)
  353. return hr;
  354. strcpy(lpDest->Value.lpszA, lpSrc->Value.lpszA);
  355. break;
  356. case PT_BINARY:
  357. if(lpSrc->Value.bin.cb > 0) {
  358. hr = lpfAllocMore(lpSrc->Value.bin.cb, lpBase, (void **) &lpDest->Value.bin.lpb);
  359. if (hr != hrSuccess)
  360. return hr;
  361. }
  362. lpDest->Value.bin.cb = lpSrc->Value.bin.cb;
  363. if(lpSrc->Value.bin.cb > 0)
  364. memcpy(lpDest->Value.bin.lpb, lpSrc->Value.bin.lpb, lpSrc->Value.bin.cb);
  365. else
  366. lpDest->Value.bin.lpb = NULL;
  367. break;
  368. case PT_CLSID:
  369. hr = lpfAllocMore(sizeof(GUID), lpBase, (void **)&lpDest->Value.lpguid);
  370. if (hr != hrSuccess)
  371. return hr;
  372. memcpy(lpDest->Value.lpguid, lpSrc->Value.lpguid, sizeof(GUID));
  373. break;
  374. case PT_ERROR:
  375. lpDest->Value.err = lpSrc->Value.err;
  376. break;
  377. case PT_SRESTRICTION:
  378. if (lpSrc->Value.lpszA == NULL)
  379. return MAPI_E_INVALID_PARAMETER;
  380. /*
  381. * NOTE: we place the object pointer in lpszA to make sure it
  382. * is on the same offset as Value.x on 32-bit as 64-bit
  383. * machines.
  384. */
  385. hr = lpfAllocMore(sizeof(SRestriction), lpBase, (void **)&lpDest->Value.lpszA);
  386. if (hr != hrSuccess)
  387. return hr;
  388. hr = Util::HrCopySRestriction((LPSRestriction)lpDest->Value.lpszA, (LPSRestriction)lpSrc->Value.lpszA, lpBase);
  389. break;
  390. case PT_ACTIONS:
  391. if (lpSrc->Value.lpszA == NULL)
  392. return MAPI_E_INVALID_PARAMETER;
  393. /*
  394. * NOTE: we place the object pointer in lpszA to make sure it
  395. * is on the same offset as Value.x on 32-bit as 64-bit
  396. * machines.
  397. */
  398. hr = lpfAllocMore(sizeof(ACTIONS), lpBase, (void **)&lpDest->Value.lpszA);
  399. if (hr != hrSuccess)
  400. return hr;
  401. hr = Util::HrCopyActions((ACTIONS *)lpDest->Value.lpszA, (ACTIONS *)lpSrc->Value.lpszA, lpBase);
  402. break;
  403. case PT_NULL:
  404. break;
  405. case PT_OBJECT:
  406. lpDest->Value.x = 0;
  407. break;
  408. // MV properties
  409. case PT_MV_I2:
  410. hr = lpfAllocMore(sizeof(short int) * lpSrc->Value.MVi.cValues, lpBase, (void **)&lpDest->Value.MVi.lpi);
  411. if (hr != hrSuccess)
  412. return hr;
  413. memcpy(lpDest->Value.MVi.lpi, lpSrc->Value.MVi.lpi, sizeof(short int) * lpSrc->Value.MVi.cValues);
  414. lpDest->Value.MVi.cValues = lpSrc->Value.MVi.cValues;
  415. break;
  416. case PT_MV_LONG:
  417. hr = lpfAllocMore(sizeof(LONG) * lpSrc->Value.MVl.cValues, lpBase, (void **)&lpDest->Value.MVl.lpl);
  418. if (hr != hrSuccess)
  419. return hr;
  420. memcpy(lpDest->Value.MVl.lpl, lpSrc->Value.MVl.lpl, sizeof(LONG) * lpSrc->Value.MVl.cValues);
  421. lpDest->Value.MVl.cValues = lpSrc->Value.MVl.cValues;
  422. break;
  423. case PT_MV_FLOAT:
  424. hr = lpfAllocMore(sizeof(float) * lpSrc->Value.MVflt.cValues, lpBase, (void **)&lpDest->Value.MVflt.lpflt);
  425. if (hr != hrSuccess)
  426. return hr;
  427. memcpy(lpDest->Value.MVflt.lpflt, lpSrc->Value.MVflt.lpflt, sizeof(float) * lpSrc->Value.MVflt.cValues);
  428. lpDest->Value.MVflt.cValues = lpSrc->Value.MVflt.cValues;
  429. break;
  430. case PT_MV_DOUBLE:
  431. case PT_MV_APPTIME:
  432. hr = lpfAllocMore(sizeof(double) * lpSrc->Value.MVdbl.cValues, lpBase, (void **)&lpDest->Value.MVdbl.lpdbl);
  433. if (hr != hrSuccess)
  434. return hr;
  435. memcpy(lpDest->Value.MVdbl.lpdbl, lpSrc->Value.MVdbl.lpdbl, sizeof(double) * lpSrc->Value.MVdbl.cValues);
  436. lpDest->Value.MVdbl.cValues = lpSrc->Value.MVdbl.cValues;
  437. break;
  438. case PT_MV_I8:
  439. hr = lpfAllocMore(sizeof(LONGLONG) * lpSrc->Value.MVli.cValues, lpBase, (void **)&lpDest->Value.MVli.lpli);
  440. if (hr != hrSuccess)
  441. return hr;
  442. memcpy(lpDest->Value.MVli.lpli, lpSrc->Value.MVli.lpli, sizeof(LONGLONG) * lpSrc->Value.MVli.cValues);
  443. lpDest->Value.MVli.cValues = lpSrc->Value.MVli.cValues;
  444. break;
  445. case PT_MV_CURRENCY:
  446. hr = lpfAllocMore(sizeof(CURRENCY) * lpSrc->Value.MVcur.cValues, lpBase, (void **)&lpDest->Value.MVcur.lpcur);
  447. if (hr != hrSuccess)
  448. return hr;
  449. memcpy(lpDest->Value.MVcur.lpcur, lpSrc->Value.MVcur.lpcur, sizeof(CURRENCY) * lpSrc->Value.MVcur.cValues);
  450. lpDest->Value.MVcur.cValues = lpSrc->Value.MVcur.cValues;
  451. break;
  452. case PT_MV_SYSTIME:
  453. hr = lpfAllocMore(sizeof(FILETIME) * lpSrc->Value.MVft.cValues, lpBase, (void **)&lpDest->Value.MVft.lpft);
  454. if (hr != hrSuccess)
  455. return hr;
  456. memcpy(lpDest->Value.MVft.lpft, lpSrc->Value.MVft.lpft, sizeof(FILETIME) * lpSrc->Value.MVft.cValues);
  457. lpDest->Value.MVft.cValues = lpSrc->Value.MVft.cValues;
  458. break;
  459. case PT_MV_STRING8:
  460. hr = lpfAllocMore(sizeof(LPSTR *) * lpSrc->Value.MVszA.cValues, lpBase, (void **)&lpDest->Value.MVszA.lppszA);
  461. if (hr != hrSuccess)
  462. return hr;
  463. for (ULONG i = 0; i < lpSrc->Value.MVszA.cValues; ++i) {
  464. int datalength = strlen(lpSrc->Value.MVszA.lppszA[i]) + 1;
  465. hr = lpfAllocMore(datalength, lpBase, (void **)&lpDest->Value.MVszA.lppszA[i]);
  466. if (hr != hrSuccess)
  467. return hr;
  468. memcpy(lpDest->Value.MVszA.lppszA[i], lpSrc->Value.MVszA.lppszA[i], datalength);
  469. }
  470. lpDest->Value.MVszA.cValues = lpSrc->Value.MVszA.cValues;
  471. break;
  472. case PT_MV_UNICODE:
  473. hr = lpfAllocMore(sizeof(LPWSTR *) * lpSrc->Value.MVszW.cValues, lpBase, (void **)&lpDest->Value.MVszW.lppszW);
  474. if (hr != hrSuccess)
  475. return hr;
  476. for (ULONG i = 0; i < lpSrc->Value.MVszW.cValues; ++i) {
  477. hr = lpfAllocMore(wcslen(lpSrc->Value.MVszW.lppszW[i]) * sizeof(WCHAR) + sizeof(WCHAR), lpBase, (void**)&lpDest->Value.MVszW.lppszW[i]);
  478. if (hr != hrSuccess)
  479. return hr;
  480. wcscpy(lpDest->Value.MVszW.lppszW[i], lpSrc->Value.MVszW.lppszW[i]);
  481. }
  482. lpDest->Value.MVszW.cValues = lpSrc->Value.MVszW.cValues;
  483. break;
  484. case PT_MV_BINARY:
  485. hr = lpfAllocMore(sizeof(SBinary) * lpSrc->Value.MVbin.cValues, lpBase, (void **)&lpDest->Value.MVbin.lpbin);
  486. if (hr != hrSuccess)
  487. return hr;
  488. for (ULONG i = 0; i < lpSrc->Value.MVbin.cValues; ++i) {
  489. hr = lpfAllocMore(lpSrc->Value.MVbin.lpbin[i].cb, lpBase, (void **)&lpDest->Value.MVbin.lpbin[i].lpb);
  490. if (hr != hrSuccess)
  491. return hr;
  492. memcpy(lpDest->Value.MVbin.lpbin[i].lpb, lpSrc->Value.MVbin.lpbin[i].lpb, lpSrc->Value.MVbin.lpbin[i].cb);
  493. lpDest->Value.MVbin.lpbin[i].cb = lpSrc->Value.MVbin.lpbin[i].cb;
  494. }
  495. lpDest->Value.MVbin.cValues = lpSrc->Value.MVbin.cValues;
  496. break;
  497. case PT_MV_CLSID:
  498. hr = lpfAllocMore(sizeof(GUID) * lpSrc->Value.MVguid.cValues, lpBase, (void **)&lpDest->Value.MVguid.lpguid);
  499. if (hr != hrSuccess)
  500. return hr;
  501. memcpy(lpDest->Value.MVguid.lpguid, lpSrc->Value.MVguid.lpguid, sizeof(GUID) * lpSrc->Value.MVguid.cValues);
  502. lpDest->Value.MVguid.cValues = lpSrc->Value.MVguid.cValues;
  503. break;
  504. default:
  505. return MAPI_E_INVALID_PARAMETER;
  506. }
  507. lpDest->ulPropTag = lpSrc->ulPropTag;
  508. return hr;
  509. }
  510. /**
  511. * Make a copy of an SRestriction structure
  512. *
  513. * @param[out] lppDest Copy of the restiction in lpSrc
  514. * @param[in] lpSrc restriction to make a copy of
  515. *
  516. * @return MAPI error code
  517. */
  518. HRESULT Util::HrCopySRestriction(LPSRestriction *lppDest,
  519. const SRestriction *lpSrc)
  520. {
  521. LPSRestriction lpDest = NULL;
  522. HRESULT hr = MAPIAllocateBuffer(sizeof(SRestriction),
  523. reinterpret_cast<void **>(&lpDest));
  524. if (hr != hrSuccess)
  525. return hr;
  526. hr = HrCopySRestriction(lpDest, lpSrc, lpDest);
  527. if(hr != hrSuccess)
  528. return hr;
  529. *lppDest = lpDest;
  530. return hrSuccess;
  531. }
  532. /**
  533. * Make a copy of an SRestriction struction on a preallocated destination
  534. *
  535. * @param[out] lppDest Copy of the restiction in lpSrc
  536. * @param[in] lpSrc restriction to make a copy of
  537. * @param[in] lpBase Base pointer to use with MAPIAllocateMore
  538. *
  539. * @return MAPI error code
  540. */
  541. HRESULT Util::HrCopySRestriction(LPSRestriction lpDest,
  542. const SRestriction *lpSrc, void *lpBase)
  543. {
  544. HRESULT hr = hrSuccess;
  545. unsigned int i;
  546. if (lpDest == NULL || lpSrc == NULL || lpBase == NULL)
  547. return MAPI_E_INVALID_PARAMETER;
  548. lpDest->rt = lpSrc->rt;
  549. switch(lpSrc->rt) {
  550. case RES_AND:
  551. lpDest->res.resAnd.cRes = lpSrc->res.resAnd.cRes;
  552. hr = MAPIAllocateMore(sizeof(SRestriction) * lpSrc->res.resAnd.cRes, lpBase, (void **)&lpDest->res.resAnd.lpRes);
  553. if (hr != hrSuccess)
  554. return hr;
  555. for (i = 0; i < lpSrc->res.resAnd.cRes; ++i) {
  556. hr = HrCopySRestriction(&lpDest->res.resAnd.lpRes[i], &lpSrc->res.resAnd.lpRes[i], lpBase);
  557. if(hr != hrSuccess)
  558. return hr;
  559. }
  560. break;
  561. case RES_OR:
  562. lpDest->res.resOr.cRes = lpSrc->res.resOr.cRes;
  563. hr = MAPIAllocateMore(sizeof(SRestriction) * lpSrc->res.resOr.cRes, lpBase, (void **)&lpDest->res.resOr.lpRes);
  564. if (hr != hrSuccess)
  565. return hr;
  566. for (i = 0; i < lpSrc->res.resOr.cRes; ++i) {
  567. hr = HrCopySRestriction(&lpDest->res.resOr.lpRes[i], &lpSrc->res.resOr.lpRes[i], lpBase);
  568. if(hr != hrSuccess)
  569. return hr;
  570. }
  571. break;
  572. case RES_NOT:
  573. hr = MAPIAllocateMore(sizeof(SRestriction), lpBase, (void **) &lpDest->res.resNot.lpRes);
  574. if (hr != hrSuccess)
  575. return hr;
  576. return HrCopySRestriction(lpDest->res.resNot.lpRes, lpSrc->res.resNot.lpRes, lpBase);
  577. case RES_CONTENT:
  578. lpDest->res.resContent.ulFuzzyLevel = lpSrc->res.resContent.ulFuzzyLevel;
  579. lpDest->res.resContent.ulPropTag = lpSrc->res.resContent.ulPropTag;
  580. hr = MAPIAllocateMore(sizeof(SPropValue), lpBase, (void **) &lpDest->res.resContent.lpProp);
  581. if (hr != hrSuccess)
  582. return hr;
  583. return HrCopyProperty(lpDest->res.resContent.lpProp, lpSrc->res.resContent.lpProp, lpBase);
  584. case RES_PROPERTY:
  585. lpDest->res.resProperty.relop = lpSrc->res.resProperty.relop;
  586. lpDest->res.resProperty.ulPropTag = lpSrc->res.resProperty.ulPropTag;
  587. hr = MAPIAllocateMore(sizeof(SPropValue), lpBase, (void **) &lpDest->res.resProperty.lpProp);
  588. if (hr != hrSuccess)
  589. return hr;
  590. return HrCopyProperty(lpDest->res.resProperty.lpProp, lpSrc->res.resProperty.lpProp, lpBase);
  591. case RES_COMPAREPROPS:
  592. lpDest->res.resCompareProps.relop = lpSrc->res.resCompareProps.relop;
  593. lpDest->res.resCompareProps.ulPropTag1 = lpSrc->res.resCompareProps.ulPropTag1;
  594. lpDest->res.resCompareProps.ulPropTag2 = lpSrc->res.resCompareProps.ulPropTag2;
  595. break;
  596. case RES_BITMASK:
  597. lpDest->res.resBitMask.relBMR = lpSrc->res.resBitMask.relBMR;
  598. lpDest->res.resBitMask.ulMask = lpSrc->res.resBitMask.ulMask;
  599. lpDest->res.resBitMask.ulPropTag = lpSrc->res.resBitMask.ulPropTag;
  600. break;
  601. case RES_SIZE:
  602. lpDest->res.resSize.cb = lpSrc->res.resSize.cb;
  603. lpDest->res.resSize.relop = lpSrc->res.resSize.relop;
  604. lpDest->res.resSize.ulPropTag = lpSrc->res.resSize.ulPropTag;
  605. break;
  606. case RES_EXIST:
  607. lpDest->res.resExist.ulPropTag = lpSrc->res.resExist.ulPropTag;
  608. break;
  609. case RES_SUBRESTRICTION:
  610. lpDest->res.resSub.ulSubObject = lpSrc->res.resSub.ulSubObject;
  611. hr = MAPIAllocateMore(sizeof(SRestriction), lpBase, (void **)&lpDest->res.resSub.lpRes);
  612. if (hr != hrSuccess)
  613. return hr;
  614. return HrCopySRestriction(lpDest->res.resSub.lpRes, lpSrc->res.resSub.lpRes, lpBase);
  615. case RES_COMMENT: // What a weird restriction type
  616. lpDest->res.resComment.cValues = lpSrc->res.resComment.cValues;
  617. lpDest->res.resComment.lpRes = NULL;
  618. if (lpSrc->res.resComment.cValues > 0)
  619. {
  620. hr = MAPIAllocateMore(sizeof(SPropValue) * lpSrc->res.resComment.cValues, lpBase, (void **) &lpDest->res.resComment.lpProp);
  621. if (hr != hrSuccess)
  622. return hr;
  623. hr = HrCopyPropertyArray(lpSrc->res.resComment.lpProp, lpSrc->res.resComment.cValues, lpDest->res.resComment.lpProp, lpBase);
  624. if (hr != hrSuccess)
  625. return hr;
  626. }
  627. if (lpSrc->res.resComment.lpRes) {
  628. hr = MAPIAllocateMore(sizeof(SRestriction), lpBase, (void **) &lpDest->res.resComment.lpRes);
  629. if (hr != hrSuccess)
  630. return hr;
  631. hr = HrCopySRestriction(lpDest->res.resComment.lpRes, lpSrc->res.resComment.lpRes, lpBase);
  632. }
  633. break;
  634. }
  635. return hr;
  636. }
  637. /**
  638. * Make a copy of an ACTIONS structure (rules) on a preallocated destination
  639. *
  640. * @param lpDest Copy of the actions in lpSrc
  641. * @param lpSrc actions to make a copy of
  642. * @param lpBase Base pointer to use with MAPIAllocateMore
  643. *
  644. * @return MAPI error code
  645. */
  646. HRESULT Util::HrCopyActions(ACTIONS *lpDest, const ACTIONS *lpSrc,
  647. void *lpBase)
  648. {
  649. unsigned int i;
  650. lpDest->cActions = lpSrc->cActions;
  651. lpDest->ulVersion = lpSrc->ulVersion;
  652. HRESULT hr = MAPIAllocateMore(sizeof(ACTION) * lpSrc->cActions, lpBase,
  653. reinterpret_cast<void **>(&lpDest->lpAction));
  654. if (hr != hrSuccess)
  655. return hr;
  656. memset(lpDest->lpAction, 0, sizeof(ACTION) * lpSrc->cActions);
  657. for (i = 0; i < lpSrc->cActions; ++i) {
  658. hr = HrCopyAction(&lpDest->lpAction[i], &lpSrc->lpAction[i], lpBase);
  659. if(hr != hrSuccess)
  660. return hr;
  661. }
  662. return hrSuccess;
  663. }
  664. /**
  665. * Make a copy of one ACTION structure (rules) on a preallocated destination
  666. *
  667. * @param lpDest Copy of the action in lpSrc
  668. * @param lpSrc action to make a copy of
  669. * @param lpBase Base pointer to use with MAPIAllocateMore
  670. *
  671. * @return MAPI error code
  672. */
  673. HRESULT Util::HrCopyAction(ACTION *lpDest, const ACTION *lpSrc, void *lpBase)
  674. {
  675. HRESULT hr = hrSuccess;
  676. lpDest->acttype = lpSrc->acttype;
  677. lpDest->ulActionFlavor = lpSrc->ulActionFlavor;
  678. lpDest->lpRes = NULL; // also unused
  679. lpDest->lpPropTagArray = NULL; // unused according to edkmdb.h
  680. lpDest->ulFlags = lpSrc->ulFlags;
  681. switch(lpSrc->acttype) {
  682. case OP_MOVE:
  683. case OP_COPY:
  684. lpDest->actMoveCopy.cbStoreEntryId = lpSrc->actMoveCopy.cbStoreEntryId;
  685. hr = MAPIAllocateMore(lpSrc->actMoveCopy.cbStoreEntryId, lpBase, (void **) &lpDest->actMoveCopy.lpStoreEntryId);
  686. if (hr != hrSuccess)
  687. return hr;
  688. memcpy(lpDest->actMoveCopy.lpStoreEntryId, lpSrc->actMoveCopy.lpStoreEntryId, lpSrc->actMoveCopy.cbStoreEntryId);
  689. lpDest->actMoveCopy.cbFldEntryId = lpSrc->actMoveCopy.cbFldEntryId;
  690. hr = MAPIAllocateMore(lpSrc->actMoveCopy.cbFldEntryId, lpBase, (void **) &lpDest->actMoveCopy.lpFldEntryId);
  691. if (hr != hrSuccess)
  692. return hr;
  693. memcpy(lpDest->actMoveCopy.lpFldEntryId, lpSrc->actMoveCopy.lpFldEntryId, lpSrc->actMoveCopy.cbFldEntryId);
  694. break;
  695. case OP_REPLY:
  696. case OP_OOF_REPLY:
  697. lpDest->actReply.cbEntryId = lpSrc->actReply.cbEntryId;
  698. hr = MAPIAllocateMore(lpSrc->actReply.cbEntryId, lpBase, (void **) &lpDest->actReply.lpEntryId);
  699. if (hr != hrSuccess)
  700. return hr;
  701. memcpy(lpDest->actReply.lpEntryId, lpSrc->actReply.lpEntryId, lpSrc->actReply.cbEntryId);
  702. lpDest->actReply.guidReplyTemplate = lpSrc->actReply.guidReplyTemplate;
  703. break;
  704. case OP_DEFER_ACTION:
  705. lpDest->actDeferAction.cbData = lpSrc->actDeferAction.cbData;
  706. hr = MAPIAllocateMore(lpSrc->actDeferAction.cbData, lpBase, (void **)&lpDest->actDeferAction.pbData);
  707. if (hr != hrSuccess)
  708. return hr;
  709. memcpy(lpDest->actDeferAction.pbData, lpSrc->actDeferAction.pbData, lpSrc->actDeferAction.cbData);
  710. break;
  711. case OP_BOUNCE:
  712. lpDest->scBounceCode = lpSrc->scBounceCode;
  713. break;
  714. case OP_FORWARD:
  715. case OP_DELEGATE:
  716. hr = MAPIAllocateMore(CbNewSRowSet(lpSrc->lpadrlist->cEntries), lpBase, reinterpret_cast<void **>(&lpDest->lpadrlist));
  717. if (hr != hrSuccess)
  718. return hr;
  719. return HrCopySRowSet((LPSRowSet)lpDest->lpadrlist, (LPSRowSet)lpSrc->lpadrlist, lpBase);
  720. case OP_TAG:
  721. return HrCopyProperty(&lpDest->propTag, &lpSrc->propTag, lpBase);
  722. case OP_DELETE:
  723. case OP_MARK_AS_READ:
  724. break;
  725. default:
  726. break;
  727. }
  728. return hr;
  729. }
  730. /**
  731. * Make a copy of a complete rowset
  732. *
  733. * @param[out] lpDest Preallocated SRowSet structure for destination. Should have enough place for lpSrc->cRows.
  734. * @param[in] lpSrc Make a copy of the rows in this set
  735. * @param[in] lpBase Use MAPIAllocateMore with this pointer
  736. *
  737. * @return MAPI error code
  738. */
  739. HRESULT Util::HrCopySRowSet(LPSRowSet lpDest, const SRowSet *lpSrc,
  740. void *lpBase)
  741. {
  742. unsigned int i;
  743. lpDest->cRows = 0;
  744. for (i = 0; i < lpSrc->cRows; ++i) {
  745. HRESULT hr = HrCopySRow(&lpDest->aRow[i], &lpSrc->aRow[i], lpBase);
  746. if (hr != hrSuccess)
  747. return hr;
  748. ++lpDest->cRows;
  749. }
  750. return hrSuccess;
  751. }
  752. /**
  753. * Make a copy one row of a rowset.
  754. *
  755. * @note According to MSDN, rows in an SRowSet should use separate
  756. * MAPIAllocate() calls (not MAPIAllocateMore) so that rows can be
  757. * freed individually. Make sure to free your RowSet with
  758. * FreeProws(), which frees the rows individually.
  759. *
  760. * However, when you have a rowset within a rowset (eg. lpadrlist in
  761. * OP_FORWARD and OP_DELEGATE rules) these need to be allocated to the
  762. * original row, and not separate
  763. *
  764. * @param[out] lpDest Preallocated destination base pointer of the new row
  765. * @param[in] lpSrc Row to make a copy of
  766. * @param[in] lpBase Optional base pointer to allocate memory for properties
  767. *
  768. * @return MAPI error code
  769. */
  770. HRESULT Util::HrCopySRow(LPSRow lpDest, const SRow *lpSrc, void *lpBase)
  771. {
  772. HRESULT hr;
  773. lpDest->cValues = lpSrc->cValues;
  774. if (lpBase)
  775. hr = MAPIAllocateMore(sizeof(SPropValue) * lpSrc->cValues, lpBase, (void **) &lpDest->lpProps);
  776. else
  777. hr = MAPIAllocateBuffer(sizeof(SPropValue) * lpSrc->cValues, (void **) &lpDest->lpProps);
  778. if (hr != hrSuccess)
  779. return hr;
  780. return HrCopyPropertyArray(lpSrc->lpProps, lpSrc->cValues, lpDest->lpProps, lpBase ? lpBase : lpDest->lpProps);
  781. }
  782. HRESULT Util::HrCopyPropTagArray(const SPropTagArray *lpSrc,
  783. LPSPropTagArray *lppDest)
  784. {
  785. SPropTagArrayPtr ptrPropTagArray;
  786. HRESULT hr = MAPIAllocateBuffer(CbNewSPropTagArray(lpSrc->cValues), &~ptrPropTagArray);
  787. if (hr != hrSuccess)
  788. return hr;
  789. memcpy(ptrPropTagArray->aulPropTag, lpSrc->aulPropTag, lpSrc->cValues * sizeof *lpSrc->aulPropTag);
  790. ptrPropTagArray->cValues = lpSrc->cValues;
  791. *lppDest = ptrPropTagArray.release();
  792. return hrSuccess;
  793. }
  794. /**
  795. * Copies a LPSPropTagArray while forcing all string types to either
  796. * PT_STRING8 or PT_UNICODE according to the MAPI_UNICODE flag in
  797. * ulFlags.
  798. *
  799. * @param[in] ulFlags 0 or MAPI_UNICODE for PT_STRING8 or PT_UNICODE proptags
  800. * @param[in] lpSrc Source SPropTagArray to copy to lppDest
  801. * @param[out] lppDest Destination SPropTagArray with fixed types for strings
  802. */
  803. HRESULT Util::HrCopyUnicodePropTagArray(ULONG ulFlags,
  804. const SPropTagArray *lpSrc, LPSPropTagArray *lppDest)
  805. {
  806. LPSPropTagArray lpPropTagArray = NULL;
  807. HRESULT hr = MAPIAllocateBuffer(CbNewSPropTagArray(lpSrc->cValues),
  808. reinterpret_cast<void **>(&lpPropTagArray));
  809. if (hr != hrSuccess)
  810. return hr;
  811. for (ULONG n = 0; n < lpSrc->cValues; ++n) {
  812. if (PROP_TYPE(lpSrc->aulPropTag[n]) == PT_STRING8 || PROP_TYPE(lpSrc->aulPropTag[n]) == PT_UNICODE)
  813. lpPropTagArray->aulPropTag[n] = CHANGE_PROP_TYPE(lpSrc->aulPropTag[n], ((ulFlags & MAPI_UNICODE) ? PT_UNICODE : PT_STRING8));
  814. else
  815. lpPropTagArray->aulPropTag[n] = lpSrc->aulPropTag[n];
  816. }
  817. lpPropTagArray->cValues = lpSrc->cValues;
  818. *lppDest = lpPropTagArray;
  819. return hrSuccess;
  820. }
  821. /**
  822. * Make a copy of a byte array using MAPIAllocate functions. This
  823. * function has a special case: when the input size is 0, the returned
  824. * pointer is not allocated and NULL is returned.
  825. *
  826. * @param[in] ulSize number of bytes in lpSrc to copy
  827. * @param[in] lpSrc bytes to copy into lppDest
  828. * @param[out] lpulDestSize number of bytes copied from lpSrc
  829. * @param[out] lppDest copied buffer
  830. * @param[in] lpBase Optional base pointer for MAPIAllocateMore
  831. *
  832. * @return MAPI error code
  833. */
  834. HRESULT Util::HrCopyBinary(ULONG ulSize, const BYTE *lpSrc,
  835. ULONG *lpulDestSize, LPBYTE *lppDest, LPVOID lpBase)
  836. {
  837. HRESULT hr;
  838. LPBYTE lpDest = NULL;
  839. if (ulSize == 0) {
  840. *lpulDestSize = 0;
  841. *lppDest = NULL;
  842. return hrSuccess;
  843. }
  844. if (lpBase)
  845. hr = MAPIAllocateMore(ulSize, lpBase, (void **)&lpDest);
  846. else
  847. hr = MAPIAllocateBuffer(ulSize, (void **) &lpDest);
  848. if (hr != hrSuccess)
  849. return hr;
  850. memcpy(lpDest, lpSrc, ulSize);
  851. *lppDest = lpDest;
  852. *lpulDestSize = ulSize;
  853. return hrSuccess;
  854. }
  855. /**
  856. * Make a copy of an EntryID. Since this actually uses HrCopyBinary, this
  857. * function has a special case: when the input size is 0, the returned
  858. * pointer is not allocated and NULL is returned.
  859. *
  860. * @param[in] ulSize size of the entryid
  861. * @param[in] lpSrc the entryid to make a copy of
  862. * @param[out] lpulDestSize output size of the entryid
  863. * @param[out] lppDest the copy of the entryid
  864. * @param[in] lpBase Optional pointer for MAPIAllocateMore
  865. *
  866. * @return MAPI Error code
  867. */
  868. HRESULT Util::HrCopyEntryId(ULONG ulSize, const ENTRYID *lpSrc,
  869. ULONG *lpulDestSize, LPENTRYID* lppDest, LPVOID lpBase)
  870. {
  871. return HrCopyBinary(ulSize, (const BYTE *)lpSrc, lpulDestSize,
  872. (LPBYTE *)lppDest, lpBase);
  873. }
  874. /*
  875. * The full checks need to be done. One cannot take a shortcut à la
  876. * "result = (a - b)", as this can lead to underflow and falsify the result.
  877. * (Consider: short a=-16385, b=16385;)
  878. */
  879. template<typename T> static int twcmp(T a, T b)
  880. {
  881. return (a < b) ? -1 : (a == b) ? 0 : 1;
  882. }
  883. /**
  884. * Compare two SBinary values.
  885. * A shorter binary value always compares less to a longer binary value. So only
  886. * when the two values are equal in size the actual data is compared.
  887. *
  888. * @param[in] left
  889. * The left SBinary value that should be compared to right.
  890. * @param[in] right
  891. * The right SBinary value that should be compared to left.
  892. *
  893. * @return integer
  894. * @retval 0 The two values are equal.
  895. * @retval <0 The left value is 'less than' the right value.
  896. * @retval >0 The left value is 'greater than' the right value.
  897. */
  898. int Util::CompareSBinary(const SBinary &sbin1, const SBinary &sbin2)
  899. {
  900. if (sbin1.lpb && sbin2.lpb && sbin1.cb > 0 && sbin1.cb == sbin2.cb)
  901. return memcmp(sbin1.lpb, sbin2.lpb, sbin1.cb);
  902. else
  903. return twcmp(sbin1.cb, sbin2.cb);
  904. }
  905. /**
  906. * Compare two properties, optionally using an ECLocale object for
  907. * correct string compares. String compares are always done case
  908. * insensitive.
  909. *
  910. * The function cannot compare different typed properties. The PR_ANR
  911. * property is a special case, where it checks for 'contains' rather
  912. * than 'is equal'.
  913. *
  914. * @param[in] lpProp1 property to compare
  915. * @param[in] lpProp2 property to compare
  916. * @param[in] locale current locale object
  917. * @param[out] lpCompareResult the compare result
  918. * 0, the properties are equal
  919. * <0, The left value is 'less than' the right value.
  920. * >0, The left value is 'greater than' the right value.
  921. *
  922. * @return MAPI Error code
  923. * @retval MAPI_E_INVALID_PARAMETER input parameters are NULL or property types are different
  924. * @retval MAPI_E_INVALID_TYPE the type of the properties is not a valid MAPI type
  925. */
  926. // @todo: Check if we need unicode string functions here
  927. HRESULT Util::CompareProp(const SPropValue *lpProp1, const SPropValue *lpProp2,
  928. const ECLocale &locale, int *lpCompareResult)
  929. {
  930. HRESULT hr = hrSuccess;
  931. int nCompareResult = 0;
  932. unsigned int i;
  933. if (lpProp1 == NULL || lpProp2 == NULL || lpCompareResult == NULL)
  934. return MAPI_E_INVALID_PARAMETER;
  935. if (PROP_TYPE(lpProp1->ulPropTag) != PROP_TYPE(lpProp2->ulPropTag))
  936. return MAPI_E_INVALID_PARAMETER;
  937. switch(PROP_TYPE(lpProp1->ulPropTag)) {
  938. case PT_I2:
  939. nCompareResult = twcmp(lpProp1->Value.i, lpProp2->Value.i);
  940. break;
  941. case PT_LONG:
  942. nCompareResult = twcmp(lpProp1->Value.ul, lpProp2->Value.ul);
  943. break;
  944. case PT_R4:
  945. nCompareResult = twcmp(lpProp1->Value.flt, lpProp2->Value.flt);
  946. break;
  947. case PT_BOOLEAN:
  948. nCompareResult = twcmp(lpProp1->Value.b, lpProp2->Value.b);
  949. break;
  950. case PT_DOUBLE:
  951. case PT_APPTIME:
  952. nCompareResult = twcmp(lpProp1->Value.dbl, lpProp2->Value.dbl);
  953. break;
  954. case PT_I8:
  955. nCompareResult = twcmp(lpProp1->Value.li.QuadPart, lpProp2->Value.li.QuadPart);
  956. break;
  957. case PT_UNICODE:
  958. if (lpProp1->Value.lpszW && lpProp2->Value.lpszW)
  959. if (lpProp2->ulPropTag == PR_ANR)
  960. nCompareResult = wcs_icontains(lpProp1->Value.lpszW, lpProp2->Value.lpszW, locale);
  961. else
  962. nCompareResult = wcs_icompare(lpProp1->Value.lpszW, lpProp2->Value.lpszW, locale);
  963. else
  964. nCompareResult = lpProp1->Value.lpszW != lpProp2->Value.lpszW;
  965. break;
  966. case PT_STRING8:
  967. if (lpProp1->Value.lpszA && lpProp2->Value.lpszA)
  968. if(lpProp2->ulPropTag == PR_ANR) {
  969. nCompareResult = str_icontains(lpProp1->Value.lpszA, lpProp2->Value.lpszA, locale);
  970. } else
  971. nCompareResult = str_icompare(lpProp1->Value.lpszA, lpProp2->Value.lpszA, locale);
  972. else
  973. nCompareResult = lpProp1->Value.lpszA != lpProp2->Value.lpszA;
  974. break;
  975. case PT_SYSTIME:
  976. case PT_CURRENCY:
  977. if (lpProp1->Value.cur.Hi == lpProp2->Value.cur.Hi)
  978. nCompareResult = twcmp(lpProp1->Value.cur.Lo, lpProp2->Value.cur.Lo);
  979. else
  980. nCompareResult = twcmp(lpProp1->Value.cur.Hi, lpProp2->Value.cur.Hi);
  981. break;
  982. case PT_BINARY:
  983. nCompareResult = CompareSBinary(lpProp1->Value.bin, lpProp2->Value.bin);
  984. break;
  985. case PT_CLSID:
  986. nCompareResult = memcmp(lpProp1->Value.lpguid, lpProp2->Value.lpguid, sizeof(GUID));
  987. break;
  988. case PT_MV_I2:
  989. if (lpProp1->Value.MVi.cValues == lpProp2->Value.MVi.cValues) {
  990. for (i = 0; i < lpProp1->Value.MVi.cValues; ++i) {
  991. nCompareResult = twcmp(lpProp1->Value.MVi.lpi[i], lpProp2->Value.MVi.lpi[i]);
  992. if(nCompareResult != 0)
  993. break;
  994. }
  995. } else
  996. nCompareResult = twcmp(lpProp1->Value.MVi.cValues, lpProp2->Value.MVi.cValues);
  997. break;
  998. case PT_MV_LONG:
  999. if (lpProp1->Value.MVl.cValues == lpProp2->Value.MVl.cValues) {
  1000. for (i = 0; i < lpProp1->Value.MVl.cValues; ++i) {
  1001. nCompareResult = twcmp(lpProp1->Value.MVl.lpl[i], lpProp2->Value.MVl.lpl[i]);
  1002. if(nCompareResult != 0)
  1003. break;
  1004. }
  1005. } else
  1006. nCompareResult = twcmp(lpProp1->Value.MVl.cValues, lpProp2->Value.MVl.cValues);
  1007. break;
  1008. case PT_MV_R4:
  1009. if (lpProp1->Value.MVflt.cValues == lpProp2->Value.MVflt.cValues) {
  1010. for (i = 0; i < lpProp1->Value.MVflt.cValues; ++i) {
  1011. nCompareResult = twcmp(lpProp1->Value.MVflt.lpflt[i], lpProp2->Value.MVflt.lpflt[i]);
  1012. if(nCompareResult != 0)
  1013. break;
  1014. }
  1015. } else
  1016. nCompareResult = twcmp(lpProp1->Value.MVflt.cValues, lpProp2->Value.MVflt.cValues);
  1017. break;
  1018. case PT_MV_DOUBLE:
  1019. case PT_MV_APPTIME:
  1020. if (lpProp1->Value.MVdbl.cValues == lpProp2->Value.MVdbl.cValues) {
  1021. for (i = 0; i < lpProp1->Value.MVdbl.cValues; ++i) {
  1022. nCompareResult = twcmp(lpProp1->Value.MVdbl.lpdbl[i], lpProp2->Value.MVdbl.lpdbl[i]);
  1023. if(nCompareResult != 0)
  1024. break;
  1025. }
  1026. } else
  1027. nCompareResult = twcmp(lpProp1->Value.MVdbl.cValues, lpProp2->Value.MVdbl.cValues);
  1028. break;
  1029. case PT_MV_I8:
  1030. if (lpProp1->Value.MVli.cValues == lpProp2->Value.MVli.cValues) {
  1031. for (i = 0; i < lpProp1->Value.MVli.cValues; ++i) {
  1032. nCompareResult = twcmp(lpProp1->Value.MVli.lpli[i].QuadPart, lpProp2->Value.MVli.lpli[i].QuadPart);
  1033. if(nCompareResult != 0)
  1034. break;
  1035. }
  1036. } else
  1037. nCompareResult = twcmp(lpProp1->Value.MVli.cValues, lpProp2->Value.MVli.cValues);
  1038. break;
  1039. case PT_MV_SYSTIME:
  1040. case PT_MV_CURRENCY:
  1041. if (lpProp1->Value.MVcur.cValues == lpProp2->Value.MVcur.cValues) {
  1042. for (i = 0; i < lpProp1->Value.MVcur.cValues; ++i) {
  1043. if(lpProp1->Value.MVcur.lpcur[i].Hi == lpProp2->Value.MVcur.lpcur[i].Hi)
  1044. nCompareResult = twcmp(lpProp1->Value.MVcur.lpcur[i].Lo, lpProp2->Value.MVcur.lpcur[i].Lo);
  1045. else
  1046. nCompareResult = twcmp(lpProp1->Value.MVcur.lpcur[i].Hi, lpProp2->Value.MVcur.lpcur[i].Hi);
  1047. if(nCompareResult != 0)
  1048. break;
  1049. }
  1050. } else
  1051. nCompareResult = lpProp1->Value.MVcur.cValues == lpProp2->Value.MVcur.cValues;
  1052. break;
  1053. case PT_MV_CLSID:
  1054. if (lpProp1->Value.MVguid.cValues == lpProp2->Value.MVguid.cValues)
  1055. nCompareResult = memcmp(lpProp1->Value.MVguid.lpguid, lpProp2->Value.MVguid.lpguid, sizeof(GUID)*lpProp1->Value.MVguid.cValues);
  1056. else
  1057. nCompareResult = twcmp(lpProp1->Value.MVguid.cValues, lpProp2->Value.MVguid.cValues);
  1058. break;
  1059. case PT_MV_BINARY:
  1060. if (lpProp1->Value.MVbin.cValues == lpProp2->Value.MVbin.cValues) {
  1061. for (i = 0; i < lpProp1->Value.MVbin.cValues; ++i) {
  1062. nCompareResult = CompareSBinary(lpProp1->Value.MVbin.lpbin[i], lpProp2->Value.MVbin.lpbin[i]);
  1063. if(nCompareResult != 0)
  1064. break;
  1065. }
  1066. } else
  1067. nCompareResult = twcmp(lpProp1->Value.MVbin.cValues, lpProp2->Value.MVbin.cValues);
  1068. break;
  1069. case PT_MV_UNICODE:
  1070. if (lpProp1->Value.MVszW.cValues == lpProp2->Value.MVszW.cValues) {
  1071. for (i = 0; i < lpProp1->Value.MVszW.cValues; ++i) {
  1072. if (lpProp1->Value.MVszW.lppszW[i] && lpProp2->Value.MVszW.lppszW[i])
  1073. nCompareResult = wcscasecmp(lpProp1->Value.MVszW.lppszW[i], lpProp2->Value.MVszW.lppszW[i]);
  1074. else
  1075. nCompareResult = lpProp1->Value.MVszW.lppszW[i] != lpProp2->Value.MVszW.lppszW[i];
  1076. if(nCompareResult != 0)
  1077. break;
  1078. }
  1079. } else
  1080. nCompareResult = twcmp(lpProp1->Value.MVszA.cValues, lpProp2->Value.MVszA.cValues);
  1081. break;
  1082. case PT_MV_STRING8:
  1083. if (lpProp1->Value.MVszA.cValues == lpProp2->Value.MVszA.cValues) {
  1084. for (i = 0; i < lpProp1->Value.MVszA.cValues; ++i) {
  1085. if (lpProp1->Value.MVszA.lppszA[i] && lpProp2->Value.MVszA.lppszA[i])
  1086. nCompareResult = strcasecmp(lpProp1->Value.MVszA.lppszA[i], lpProp2->Value.MVszA.lppszA[i]);
  1087. else
  1088. nCompareResult = lpProp1->Value.MVszA.lppszA[i] != lpProp2->Value.MVszA.lppszA[i];
  1089. if(nCompareResult != 0)
  1090. break;
  1091. }
  1092. } else
  1093. nCompareResult = twcmp(lpProp1->Value.MVszA.cValues, lpProp2->Value.MVszA.cValues);
  1094. break;
  1095. default:
  1096. return MAPI_E_INVALID_TYPE;
  1097. }
  1098. *lpCompareResult = nCompareResult;
  1099. return hr;
  1100. }
  1101. /**
  1102. * Calculates the number of bytes the property uses of memory,
  1103. * excluding the SPropValue struct itself, and any pointers required in this struct.
  1104. *
  1105. * @param[in] lpProp The property to calculate the size of
  1106. *
  1107. * @return size of the property
  1108. */
  1109. unsigned int Util::PropSize(const SPropValue *lpProp)
  1110. {
  1111. unsigned int ulSize, i;
  1112. if(lpProp == NULL)
  1113. return 0;
  1114. switch(PROP_TYPE(lpProp->ulPropTag)) {
  1115. case PT_I2:
  1116. return 2;
  1117. case PT_BOOLEAN:
  1118. case PT_R4:
  1119. case PT_LONG:
  1120. return 4;
  1121. case PT_APPTIME:
  1122. case PT_DOUBLE:
  1123. case PT_I8:
  1124. return 8;
  1125. case PT_UNICODE:
  1126. return lpProp->Value.lpszW ? wcslen(lpProp->Value.lpszW) : 0;
  1127. case PT_STRING8:
  1128. return lpProp->Value.lpszA ? strlen(lpProp->Value.lpszA) : 0;
  1129. case PT_SYSTIME:
  1130. case PT_CURRENCY:
  1131. return 8;
  1132. case PT_BINARY:
  1133. return lpProp->Value.bin.cb;
  1134. case PT_CLSID:
  1135. return sizeof(GUID);
  1136. case PT_MV_I2:
  1137. return 2 * lpProp->Value.MVi.cValues;
  1138. case PT_MV_R4:
  1139. return 4 * lpProp->Value.MVflt.cValues;
  1140. case PT_MV_LONG:
  1141. return 4 * lpProp->Value.MVl.cValues;
  1142. case PT_MV_APPTIME:
  1143. case PT_MV_DOUBLE:
  1144. return 8 * lpProp->Value.MVdbl.cValues;
  1145. case PT_MV_I8:
  1146. return 8 * lpProp->Value.MVli.cValues;
  1147. case PT_MV_UNICODE:
  1148. ulSize = 0;
  1149. for (i = 0; i < lpProp->Value.MVszW.cValues; ++i)
  1150. ulSize += (lpProp->Value.MVszW.lppszW[i]) ? wcslen(lpProp->Value.MVszW.lppszW[i]) : 0;
  1151. return ulSize;
  1152. case PT_MV_STRING8:
  1153. ulSize = 0;
  1154. for (i = 0; i < lpProp->Value.MVszA.cValues; ++i)
  1155. ulSize += (lpProp->Value.MVszA.lppszA[i]) ? strlen(lpProp->Value.MVszA.lppszA[i]) : 0;
  1156. return ulSize;
  1157. case PT_MV_SYSTIME:
  1158. case PT_MV_CURRENCY:
  1159. return 8 * lpProp->Value.MVcur.cValues;
  1160. case PT_MV_BINARY:
  1161. ulSize = 0;
  1162. for (i = 0; i < lpProp->Value.MVbin.cValues; ++i)
  1163. ulSize+= lpProp->Value.MVbin.lpbin[i].cb;
  1164. return ulSize;
  1165. case PT_MV_CLSID:
  1166. return sizeof(GUID) * lpProp->Value.MVguid.cValues;
  1167. default:
  1168. return 0;
  1169. }
  1170. }
  1171. /**
  1172. * Convert plaintext to HTML using streams.
  1173. *
  1174. * Converts the text stream to HTML, and writes in the html stream.
  1175. * Both streams will be at the end on return. This function does no
  1176. * error checking. If a character in the text cannot be represented
  1177. * in the given codepage, it will make a unicode HTML entity instead.
  1178. *
  1179. * @param[in] text IStream object as plain text input, must be PT_UNICODE
  1180. * @param[out] html IStream object for HTML output
  1181. * @param[in] ulCodePage Codepage to convert HTML stream to
  1182. *
  1183. * @return HRESULT Mapi error code, from IMemStream
  1184. * @retval MAPI_E_NOT_FOUND No suitable charset found for given ulCodepage
  1185. * @retval MAPI_E_BAD_CHARWIDTH Iconv error
  1186. */
  1187. // @todo please optimize function, quite slow. (mostly due to HtmlEntityFromChar)
  1188. #define BUFSIZE 65536
  1189. HRESULT Util::HrTextToHtml(IStream *text, IStream *html, ULONG ulCodepage)
  1190. {
  1191. HRESULT hr = hrSuccess;
  1192. ULONG cRead;
  1193. std::wstring strHtml;
  1194. WCHAR lpBuffer[BUFSIZE];
  1195. static const char header1[] = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n" \
  1196. "<HTML>\n" \
  1197. "<HEAD>\n" \
  1198. "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=";
  1199. // inserts charset in header here
  1200. static const char header2[] = "\">\n" \
  1201. "<META NAME=\"Generator\" CONTENT=\"Kopano HTML builder 1.0\">\n" \
  1202. "<TITLE></TITLE>\n" \
  1203. "</HEAD>\n" \
  1204. "<BODY>\n" \
  1205. "<!-- Converted from text/plain format -->\n" \
  1206. "\n" \
  1207. "<P><FONT STYLE=\"font-family: courier\" SIZE=2>\n";
  1208. static const char footer[] = "</FONT>\n" \
  1209. "</P>\n" \
  1210. "\n" \
  1211. "</BODY>" \
  1212. "</HTML>";
  1213. ULONG i = 0;
  1214. size_t stRead = 0;
  1215. size_t stWrite = 0;
  1216. size_t stWritten;
  1217. size_t err;
  1218. const char *readBuffer = NULL;
  1219. std::unique_ptr<char[]> writeBuffer;
  1220. char *wPtr = NULL;
  1221. iconv_t cd = (iconv_t)-1;
  1222. const char *lpszCharset;
  1223. hr = HrGetCharsetByCP(ulCodepage, &lpszCharset);
  1224. if (hr != hrSuccess) {
  1225. // client actually should have set the PR_INTERNET_CPID to the correct value
  1226. lpszCharset = "us-ascii";
  1227. hr = hrSuccess;
  1228. }
  1229. cd = iconv_open(lpszCharset, CHARSET_WCHAR);
  1230. if (cd == (iconv_t)-1) {
  1231. hr = MAPI_E_BAD_CHARWIDTH;
  1232. goto exit;
  1233. }
  1234. writeBuffer.reset(new(std::nothrow) char[BUFSIZE * 2]);
  1235. if (writeBuffer == nullptr) {
  1236. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  1237. goto exit;
  1238. }
  1239. // @todo, run this through iconv aswell?
  1240. hr = html->Write(header1, strlen(header1), NULL);
  1241. if (hr != hrSuccess)
  1242. goto exit;
  1243. hr = html->Write(lpszCharset, strlen(lpszCharset), NULL);
  1244. if (hr != hrSuccess)
  1245. goto exit;
  1246. hr = html->Write(header2, strlen(header2), NULL);
  1247. if (hr != hrSuccess)
  1248. goto exit;
  1249. while (1) {
  1250. strHtml.clear();
  1251. hr = text->Read(lpBuffer, BUFSIZE*sizeof(WCHAR), &cRead);
  1252. if (hr != hrSuccess)
  1253. goto exit;
  1254. if (cRead == 0)
  1255. break;
  1256. cRead /= sizeof(WCHAR);
  1257. // escape some characters in HTML
  1258. for (i = 0; i < cRead; ++i) {
  1259. if (lpBuffer[i] != ' ') {
  1260. std::wstring str;
  1261. CHtmlEntity::CharToHtmlEntity(lpBuffer[i], str);
  1262. strHtml += str;
  1263. continue;
  1264. }
  1265. if ((i + 1) < cRead && lpBuffer[i+1] == ' ')
  1266. strHtml += L"&nbsp;";
  1267. else
  1268. strHtml += L" ";
  1269. }
  1270. /* Convert WCHAR to wanted (8-bit) charset */
  1271. readBuffer = (const char*)strHtml.c_str();
  1272. stRead = strHtml.size() * sizeof(WCHAR);
  1273. while (stRead > 0) {
  1274. wPtr = writeBuffer.get();
  1275. stWrite = BUFSIZE * 2;
  1276. err = iconv(cd, iconv_HACK(&readBuffer), &stRead, &wPtr, &stWrite);
  1277. stWritten = (BUFSIZE * 2) - stWrite;
  1278. // write to stream
  1279. hr = html->Write(writeBuffer.get(), stWritten, NULL);
  1280. if (hr != hrSuccess)
  1281. goto exit;
  1282. if (err != static_cast<size_t>(-1))
  1283. continue;
  1284. // make html number from WCHAR entry
  1285. std::string strHTMLUnicode = "&#";
  1286. strHTMLUnicode += stringify(*(WCHAR*)readBuffer);
  1287. strHTMLUnicode += ";";
  1288. hr = html->Write(strHTMLUnicode.c_str(), strHTMLUnicode.length(), NULL);
  1289. if (hr != hrSuccess)
  1290. goto exit;
  1291. // skip unknown character
  1292. readBuffer += sizeof(WCHAR);
  1293. stRead -= sizeof(WCHAR);
  1294. }
  1295. }
  1296. // @todo, run through iconv?
  1297. hr = html->Write(footer, strlen(footer), NULL);
  1298. exit:
  1299. if (cd != (iconv_t)-1)
  1300. iconv_close(cd);
  1301. return hr;
  1302. }
  1303. /**
  1304. * Convert a plain-text widestring text to html data in specific
  1305. * codepage. No html headers or footers are set in the string like the
  1306. * stream version does.
  1307. *
  1308. * @param[in] text plaintext to convert to html
  1309. * @param[out] strHTML append to this html string
  1310. * @param[in] ulCodepage html will be in this codepage
  1311. *
  1312. * @return MAPI Error code
  1313. */
  1314. HRESULT Util::HrTextToHtml(const WCHAR *text, std::string &strHTML, ULONG ulCodepage)
  1315. {
  1316. HRESULT hr = hrSuccess;
  1317. const char *lpszCharset;
  1318. wstring wHTML;
  1319. hr = HrGetCharsetByCP(ulCodepage, &lpszCharset);
  1320. if (hr != hrSuccess) {
  1321. // client actually should have set the PR_INTERNET_CPID to the correct value
  1322. lpszCharset = "us-ascii";
  1323. hr = hrSuccess;
  1324. }
  1325. // escape some characters in HTML
  1326. for (ULONG i = 0; text[i] != '\0'; ++i) {
  1327. if (text[i] != ' ') {
  1328. std::wstring str;
  1329. CHtmlEntity::CharToHtmlEntity(text[i], str);
  1330. wHTML += str;
  1331. continue;
  1332. }
  1333. if (text[i+1] == ' ')
  1334. wHTML += L"&nbsp;";
  1335. else
  1336. wHTML += L" ";
  1337. }
  1338. try {
  1339. strHTML += convert_to<string>(lpszCharset, wHTML, rawsize(wHTML), CHARSET_WCHAR);
  1340. } catch (const convert_exception &) {
  1341. }
  1342. return hr;
  1343. }
  1344. static const struct _rtfcodepages {
  1345. int id; // RTF codepage ID
  1346. ULONG ulCodepage; // Windows codepage
  1347. } RTFCODEPAGES[] = {
  1348. {437, 437}, // United States IBM
  1349. {708, 0}, // Arabic (ASMO 708)
  1350. {709, 0}, // Arabic (ASMO 449+, BCON V4)
  1351. {710, 0}, // Arabic (transparent Arabic)
  1352. {711, 0}, // Arabic (Nafitha Enhanced)
  1353. {720, 0}, // Arabic (transparent ASMO)
  1354. {819, 0}, // Windows 3.1 (United States and Western Europe)
  1355. {850, 1252}, // IBM multilingual
  1356. {852, 1251}, // Eastern European
  1357. {860, 0}, // Portuguese
  1358. {862, 0}, // Hebrew
  1359. {863, 0}, // French Canadian
  1360. {864, 0}, // Arabic
  1361. {865, 0}, // Norwegian
  1362. {866, 0}, // Soviet Union
  1363. {874, 0}, // Thai
  1364. {932, 50220}, // Japanese
  1365. {936, 936}, // Simplified Chinese
  1366. {949, 0}, // Korean
  1367. {950, 0}, // Traditional Chinese
  1368. {1250, 0}, // Windows 3.1 (Eastern European)
  1369. {1251, 0}, // Windows 3.1 (Cyrillic)
  1370. {1252, 0}, // Western European
  1371. {1253, 0}, // Greek
  1372. {1254, 0}, // Turkish
  1373. {1255, 0}, // Hebrew
  1374. {1256, 0}, // Arabic
  1375. {1257, 0}, // Baltic
  1376. {1258, 0}, // Vietnamese
  1377. {1361, 0}, // Johab
  1378. };
  1379. /**
  1380. * Convert plaintext to uncompressed RTF using streams.
  1381. *
  1382. * Converts the text stream to RTF, and writes in the rtf stream.
  1383. * Both streams will be at the end on return. This function does no
  1384. * error checking.
  1385. *
  1386. * @param[in] text IStream object as plain text input, must be PT_UNICODE
  1387. * @param[out] rtf IStream object for RTF output
  1388. *
  1389. * @return HRESULT hrSuccess
  1390. */
  1391. // @todo: remove this shizzle ?
  1392. HRESULT Util::HrTextToRtf(IStream *text, IStream *rtf)
  1393. {
  1394. ULONG cRead;
  1395. WCHAR c[BUFSIZE];
  1396. static const char header[] = "{\\rtf1\\ansi\\ansicpg1252\\fromtext \\deff0{\\fonttbl\n" \
  1397. "{\\f0\\fswiss Arial;}\n" \
  1398. "{\\f1\\fmodern Courier New;}\n" \
  1399. "{\\f2\\fnil\\fcharset2 Symbol;}\n" \
  1400. "{\\f3\\fmodern\\fcharset0 Courier New;}}\n" \
  1401. "{\\colortbl\\red0\\green0\\blue0;\\red0\\green0\\blue255;}\n" \
  1402. "\\uc1\\pard\\plain\\deftab360 \\f0\\fs20 ";
  1403. static const char footer[] = "}";
  1404. ULONG i = 0;
  1405. rtf->Write(header, strlen(header), NULL);
  1406. while(1) {
  1407. text->Read(c, BUFSIZE * sizeof(WCHAR), &cRead);
  1408. if(cRead == 0)
  1409. break;
  1410. cRead /= sizeof(WCHAR);
  1411. for (i = 0; i < cRead; ++i) {
  1412. switch (c[i]) {
  1413. case 0:
  1414. break;
  1415. case '\r':
  1416. break;
  1417. case '\n':
  1418. rtf->Write("\\line\n", 6, nullptr);
  1419. break;
  1420. case '\\':
  1421. rtf->Write("\\\\",2,NULL);
  1422. break;
  1423. case '{':
  1424. rtf->Write("\\{",2,NULL);
  1425. break;
  1426. case '}':
  1427. rtf->Write("\\}",2,NULL);
  1428. break;
  1429. case '\t':
  1430. rtf->Write("\\tab ",5,NULL);
  1431. break;
  1432. case '\f': // formfeed, ^L
  1433. rtf->Write("\\page\n",6,NULL);
  1434. break;
  1435. default:
  1436. if (c[i] < ' ' || (c[i] > 127 && c[i] <= 255)) {
  1437. char hex[16];
  1438. snprintf(hex, 16, "\\'%X", c[i]);
  1439. rtf->Write(hex, strlen(hex), NULL);
  1440. } else if (c[i] > 255) {
  1441. // make a unicode char. in signed short
  1442. char hex[16];
  1443. snprintf(hex, 16, "\\u%hd ?", (signed short)c[i]); // %hd is signed short (h is the inverse of l modifier)
  1444. rtf->Write(hex, strlen(hex), NULL);
  1445. } else {
  1446. rtf->Write(&c[i], 1, NULL);
  1447. }
  1448. }
  1449. }
  1450. }
  1451. rtf->Write(footer, strlen(footer), NULL);
  1452. return hrSuccess;
  1453. }
  1454. /**
  1455. * Find a given property tag in an array of property tags. Use
  1456. * PT_UNSPECIFIED in the proptype to find the first matching property
  1457. * id in the property array.
  1458. *
  1459. * @param[in] lpPropTags The property tag array to search in
  1460. * @param[in] ulPropTag The property tag to search for
  1461. *
  1462. * @return index in the lpPropTags array
  1463. */
  1464. LONG Util::FindPropInArray(const SPropTagArray *lpPropTags, ULONG ulPropTag)
  1465. {
  1466. unsigned int i = 0;
  1467. if (!lpPropTags)
  1468. return -1;
  1469. for (i = 0; i < lpPropTags->cValues; ++i) {
  1470. if(lpPropTags->aulPropTag[i] == ulPropTag)
  1471. break;
  1472. if(PROP_TYPE(ulPropTag) == PT_UNSPECIFIED && PROP_ID(lpPropTags->aulPropTag[i]) == PROP_ID(ulPropTag))
  1473. break;
  1474. }
  1475. if(i != lpPropTags->cValues)
  1476. return i;
  1477. return -1;
  1478. }
  1479. /**
  1480. * Return a human readable string for a specified HRESULT code. You
  1481. * should only call this function if hr contains an error. If an error
  1482. * code is not specified in this function, it will return 'access
  1483. * denied' as default error string.
  1484. *
  1485. * @param[in] hr return string version for the given value.
  1486. * @param[out] lppszError Pointer to a character pointer that will contain the error
  1487. * message on success. If no lpBase was provided, the result
  1488. * must be freed with MAPIFreeBuffer.
  1489. * @param[in] lpBase optional base pointer for use with MAPIAllocateMore.
  1490. *
  1491. * @retval hrSuccess on success.
  1492. */
  1493. HRESULT Util::HrMAPIErrorToText(HRESULT hr, LPTSTR *lppszError, void *lpBase)
  1494. {
  1495. tstring strError;
  1496. LPCTSTR lpszError = NULL;
  1497. if (lppszError == NULL)
  1498. return MAPI_E_INVALID_PARAMETER;
  1499. switch(hr)
  1500. {
  1501. case MAPI_E_END_OF_SESSION:
  1502. lpszError = _("End of Session");
  1503. break;
  1504. case MAPI_E_NETWORK_ERROR:
  1505. lpszError = _("Connection lost");
  1506. break;
  1507. case MAPI_E_NO_ACCESS:
  1508. lpszError = _("Access denied");
  1509. break;
  1510. case MAPI_E_FOLDER_CYCLE:
  1511. lpszError = _("Unable to move or copy folders. Can't copy folder. A top-level can't be copied to one of its subfolders. Or, you may not have appropriate permissions for the folder. To check your permissions for the folder, right-click the folder, and then click Properties on the shortcut menu.");
  1512. break;
  1513. case MAPI_E_STORE_FULL:
  1514. lpszError = _("The message store has reached its maximum size. To reduce the amount of data in this message store, select some items that you no longer need, and permanently (SHIFT + DEL) delete them.");
  1515. break;
  1516. case MAPI_E_USER_CANCEL:
  1517. lpszError = _("The user canceled the operation, typically by clicking the Cancel button in a dialog box.");
  1518. break;
  1519. case MAPI_E_LOGON_FAILED:
  1520. lpszError = _("A logon session could not be established.");
  1521. break;
  1522. case MAPI_E_COLLISION:
  1523. lpszError = _("The name of the folder being moved or copied is the same as that of a subfolder in the destination folder. The message store provider requires that folder names be unique. The operation stops without completing.");
  1524. break;
  1525. case MAPI_W_PARTIAL_COMPLETION:
  1526. lpszError = _("The operation succeeded, but not all entries were successfully processed, copied, deleted or moved");// emptied
  1527. break;
  1528. case MAPI_E_UNCONFIGURED:
  1529. lpszError = _("The provider does not have enough information to complete the logon. Or, the service provider has not been configured.");
  1530. break;
  1531. case MAPI_E_FAILONEPROVIDER:
  1532. lpszError = _("One of the providers cannot log on, but this error should not disable the other services.");
  1533. break;
  1534. case MAPI_E_DISK_ERROR:
  1535. lpszError = _("A database error or I/O error has occurred.");
  1536. break;
  1537. case MAPI_E_HAS_FOLDERS:
  1538. lpszError = _("The subfolder being deleted contains subfolders.");
  1539. break;
  1540. case MAPI_E_HAS_MESSAGES:
  1541. lpszError = _("The subfolder being deleted contains messages.");
  1542. break;
  1543. default: {
  1544. strError = _("No description available.");
  1545. strError.append(1, ' ');
  1546. strError.append(_("MAPI error code:"));
  1547. strError.append(1, ' ');
  1548. strError.append(tstringify(hr, true));
  1549. lpszError = strError.c_str();
  1550. }
  1551. break;
  1552. }
  1553. if (lpBase == NULL)
  1554. hr = MAPIAllocateBuffer((_tcslen(lpszError) + 1) * sizeof *lpszError, (void**)lppszError);
  1555. else
  1556. hr = MAPIAllocateMore((_tcslen(lpszError) + 1) * sizeof *lpszError, lpBase, (void**)lppszError);
  1557. if (hr != hrSuccess)
  1558. return hr;
  1559. _tcscpy(*lppszError, lpszError);
  1560. return hrSuccess;
  1561. }
  1562. /**
  1563. * Checks for invalid data in a proptag array. NULL input is
  1564. * considered valid. Any known property type is considered valid,
  1565. * including PT_UNSPECIFIED, PT_NULL and PT_ERROR.
  1566. *
  1567. * @param[in] lpPropTagArray property tag array to validate
  1568. *
  1569. * @return true for valid, false for invalid
  1570. */
  1571. bool Util::ValidatePropTagArray(const SPropTagArray *lpPropTagArray)
  1572. {
  1573. bool bResult = false;
  1574. unsigned int i;
  1575. if (lpPropTagArray == NULL)
  1576. return true;
  1577. for (i = 0; i < lpPropTagArray->cValues; ++i) {
  1578. switch (PROP_TYPE(lpPropTagArray->aulPropTag[i]))
  1579. {
  1580. case PT_UNSPECIFIED:
  1581. case PT_NULL:
  1582. case PT_I2:
  1583. case PT_I4:
  1584. case PT_R4:
  1585. case PT_R8:
  1586. case PT_BOOLEAN:
  1587. case PT_CURRENCY:
  1588. case PT_APPTIME:
  1589. case PT_SYSTIME:
  1590. case PT_I8:
  1591. case PT_STRING8:
  1592. case PT_BINARY:
  1593. case PT_UNICODE:
  1594. case PT_CLSID:
  1595. case PT_OBJECT:
  1596. case PT_MV_I2:
  1597. case PT_MV_LONG:
  1598. case PT_MV_R4:
  1599. case PT_MV_DOUBLE:
  1600. case PT_MV_CURRENCY:
  1601. case PT_MV_APPTIME:
  1602. case PT_MV_SYSTIME:
  1603. case PT_MV_BINARY:
  1604. case PT_MV_STRING8:
  1605. case PT_MV_UNICODE:
  1606. case PT_MV_CLSID:
  1607. case PT_MV_I8:
  1608. case PT_ERROR:
  1609. bResult = true;
  1610. break;
  1611. default:
  1612. return false;
  1613. }
  1614. }
  1615. return bResult;
  1616. }
  1617. /**
  1618. * Append the full contents of a stream into a std::string. It might use
  1619. * the ECMemStream interface if available, otherwise it will do a
  1620. * normal stream copy. The position of the stream on return is not
  1621. * stable.
  1622. *
  1623. * @param[in] sInput The stream to copy data from
  1624. * @param[in] strOutput The string to place data in
  1625. *
  1626. * @return MAPI Error code
  1627. */
  1628. HRESULT Util::HrStreamToString(IStream *sInput, std::string &strOutput) {
  1629. HRESULT hr = hrSuccess;
  1630. object_ptr<ECMemStream> lpMemStream;
  1631. ULONG ulRead = 0;
  1632. char buffer[BUFSIZE];
  1633. LARGE_INTEGER zero = {{0,0}};
  1634. if (sInput->QueryInterface(IID_ECMemStream, &~lpMemStream) == hrSuccess) {
  1635. // getsize, getbuffer, assign
  1636. strOutput.append(lpMemStream->GetBuffer(), lpMemStream->GetSize());
  1637. return hr;
  1638. }
  1639. // manual copy
  1640. hr = sInput->Seek(zero, SEEK_SET, NULL);
  1641. if (hr != hrSuccess)
  1642. return hr;
  1643. while (1) {
  1644. hr = sInput->Read(buffer, BUFSIZE, &ulRead);
  1645. if (hr != hrSuccess || ulRead == 0)
  1646. break;
  1647. strOutput.append(buffer, ulRead);
  1648. }
  1649. return hr;
  1650. }
  1651. /**
  1652. * Append the full contents of a stream into a std::string. It might use
  1653. * the ECMemStream interface if available, otherwise it will do a
  1654. * normal stream copy. The position of the stream on return is not
  1655. * stable.
  1656. *
  1657. * @param[in] sInput The stream to copy data from
  1658. * @param[in] strOutput The string to place data in
  1659. *
  1660. * @return MAPI Error code
  1661. */
  1662. HRESULT Util::HrStreamToString(IStream *sInput, std::wstring &strOutput) {
  1663. HRESULT hr = hrSuccess;
  1664. object_ptr<ECMemStream> lpMemStream;
  1665. ULONG ulRead = 0;
  1666. char buffer[BUFSIZE];
  1667. LARGE_INTEGER zero = {{0,0}};
  1668. if (sInput->QueryInterface(IID_ECMemStream, &~lpMemStream) == hrSuccess) {
  1669. // getsize, getbuffer, assign
  1670. strOutput.append((WCHAR*)lpMemStream->GetBuffer(), lpMemStream->GetSize() / sizeof(WCHAR));
  1671. return hr;
  1672. }
  1673. // manual copy
  1674. hr = sInput->Seek(zero, SEEK_SET, NULL);
  1675. if (hr != hrSuccess)
  1676. return hr;
  1677. while (1) {
  1678. hr = sInput->Read(buffer, BUFSIZE, &ulRead);
  1679. if (hr != hrSuccess || ulRead == 0)
  1680. break;
  1681. strOutput.append((WCHAR*)buffer, ulRead / sizeof(WCHAR));
  1682. }
  1683. return hr;
  1684. }
  1685. /**
  1686. * Convert a byte stream from ulCodepage to WCHAR.
  1687. *
  1688. * @param[in] sInput Input stream
  1689. * @param[in] ulCodepage codepage of input stream
  1690. * @param[out] lppwOutput output in WCHAR
  1691. * @return MAPI error code
  1692. */
  1693. HRESULT Util::HrConvertStreamToWString(IStream *sInput, ULONG ulCodepage, std::wstring *wstrOutput)
  1694. {
  1695. const char *lpszCharset;
  1696. convert_context converter;
  1697. string data;
  1698. HRESULT hr = HrGetCharsetByCP(ulCodepage, &lpszCharset);
  1699. if (hr != hrSuccess) {
  1700. lpszCharset = "us-ascii";
  1701. hr = hrSuccess;
  1702. }
  1703. hr = HrStreamToString(sInput, data);
  1704. if (hr != hrSuccess)
  1705. return hr;
  1706. try {
  1707. wstrOutput->assign(converter.convert_to<wstring>(CHARSET_WCHAR"//IGNORE", data, rawsize(data), lpszCharset));
  1708. } catch (std::exception &) {
  1709. return MAPI_E_INVALID_PARAMETER;
  1710. }
  1711. return hrSuccess;
  1712. }
  1713. /**
  1714. * Converts HTML (PT_BINARY, with specified codepage) to plain text (PT_UNICODE)
  1715. *
  1716. * @param[in] html IStream to PR_HTML
  1717. * @param[out] text IStream to PR_BODY_W
  1718. * @param[in] ulCodepage codepage of html stream
  1719. * @return HRESULT MAPI error code
  1720. */
  1721. HRESULT Util::HrHtmlToText(IStream *html, IStream *text, ULONG ulCodepage)
  1722. {
  1723. std::wstring wstrHTML;
  1724. CHtmlToTextParser parser;
  1725. HRESULT hr = HrConvertStreamToWString(html, ulCodepage, &wstrHTML);
  1726. if(hr != hrSuccess)
  1727. return hr;
  1728. if (!parser.Parse(string_strip_nuls(wstrHTML).c_str()))
  1729. return MAPI_E_CORRUPT_DATA;
  1730. std::wstring &strText = parser.GetText();
  1731. return text->Write(strText.data(), (strText.size()+1)*sizeof(WCHAR), NULL);
  1732. }
  1733. /**
  1734. * This converts from HTML to RTF by doing to following:
  1735. *
  1736. * Always escape { and } to \{ and \}
  1737. * Always escape \r\n to \par (dfq?)
  1738. * All HTML tags are converted from, say <BODY onclick=bla> to \r\n{\htmltagX <BODY onclick=bla>}
  1739. * Each tag with text content gets an extra {\htmltag64} to suppress generated <P>'s in the final HTML output
  1740. * Some tags output \htmlrtf \par \htmlrtf0 so that the plaintext version of the RTF has newlines in the right places
  1741. * Some effort is done so that data between <STYLE> tags is output as a single entity
  1742. * <!-- and --> tags are supported and output as a single htmltagX entity
  1743. *
  1744. * This gives an RTF stream that converts back to the original HTML when viewed in OL, but also preserves plaintext content
  1745. * when all HTML content is removed.
  1746. *
  1747. * @param[in] strHTML HTML string in WCHAR for uniformity
  1748. * @param[out] strRTF RTF output, containing unicode chars
  1749. * @return mapi error code
  1750. */
  1751. HRESULT Util::HrHtmlToRtf(const WCHAR *lpwHTML, std::string &strRTF)
  1752. {
  1753. int tag = 0, type = 0;
  1754. stack<unsigned int> stackTag;
  1755. size_t pos = 0;
  1756. bool inTag = false;
  1757. int ulCommentMode = 0; // 0=no comment, 1=just starting top-level comment, 2=inside comment level 1, 3=inside comment level 2, etc
  1758. int ulStyleMode = 0;
  1759. int ulParMode = 0;
  1760. bool bFirstText = true;
  1761. bool bPlainCRLF = false;
  1762. if (lpwHTML == NULL)
  1763. return MAPI_E_INVALID_PARAMETER;
  1764. // @todo default codepage is set on windows-1252, but is this correct for non-western outlooks?
  1765. strRTF = "{\\rtf1\\ansi\\ansicpg1252\\fromhtml1 \\deff0{\\fonttbl\r\n"
  1766. "{\\f0\\fswiss\\fcharset0 Arial;}\r\n"
  1767. "{\\f1\\fmodern Courier New;}\r\n"
  1768. "{\\f2\\fnil\\fcharset2 Symbol;}\r\n"
  1769. "{\\f3\\fmodern\\fcharset0 Courier New;}\r\n"
  1770. "{\\f4\\fswiss\\fcharset0 Arial;}\r\n"
  1771. "{\\f5\\fswiss Tahoma;}\r\n"
  1772. "{\\f6\\fswiss\\fcharset0 Times New Roman;}}\r\n"
  1773. "{\\colortbl\\red0\\green0\\blue0;\\red0\\green0\\blue255;\\red0\\green0\\blue255;}\r\n"
  1774. "\\uc1\\pard\\plain\\deftab360 \\f0\\fs24 ";
  1775. // \\uc1 is important, for characters that are not supported by the reader of this rtf.
  1776. stackTag.push(RTF_OUTHTML);
  1777. // We 'convert' from HTML to text by doing a rather simple stripping
  1778. // of tags, and conversion of some strings
  1779. while (lpwHTML[pos]) {
  1780. type = RTF_TAG_TYPE_UNK;
  1781. // Remember if this tag should output a CRLF
  1782. if(lpwHTML[pos] == '<') {
  1783. bPlainCRLF = false;
  1784. // Process important tags first
  1785. if(StrCaseCompare(lpwHTML, L"<HTML", pos)) {
  1786. type = RTF_TAG_TYPE_HTML;
  1787. } else if(StrCaseCompare(lpwHTML, L"</HTML", pos)) {
  1788. type = RTF_TAG_TYPE_HTML;
  1789. } else if(StrCaseCompare(lpwHTML, L"<HEAD", pos)) {
  1790. type = RTF_TAG_TYPE_HEAD;
  1791. } else if(StrCaseCompare(lpwHTML, L"</HEAD", pos)) {
  1792. type = RTF_TAG_TYPE_HEAD;
  1793. } else if(StrCaseCompare(lpwHTML, L"<BODY", pos)) {
  1794. type = RTF_TAG_TYPE_BODY;
  1795. } else if(StrCaseCompare(lpwHTML, L"</BODY", pos)) {
  1796. type = RTF_TAG_TYPE_BODY;
  1797. } else if(StrCaseCompare(lpwHTML, L"<P", pos)) {
  1798. type = RTF_TAG_TYPE_P;
  1799. bPlainCRLF = true;
  1800. } else if(StrCaseCompare(lpwHTML, L"</P", pos)) {
  1801. type = RTF_TAG_TYPE_P;
  1802. bPlainCRLF = true;
  1803. } else if(StrCaseCompare(lpwHTML, L"<DIV", pos)) {
  1804. type = RTF_TAG_TYPE_ENDP;
  1805. } else if(StrCaseCompare(lpwHTML, L"</DIV", pos)) {
  1806. type = RTF_TAG_TYPE_ENDP;
  1807. bPlainCRLF = true;
  1808. } else if(StrCaseCompare(lpwHTML, L"<SPAN", pos)) {
  1809. type = RTF_TAG_TYPE_STARTP;
  1810. } else if(StrCaseCompare(lpwHTML, L"</SPAN", pos)) {
  1811. type = RTF_TAG_TYPE_STARTP;
  1812. } else if(StrCaseCompare(lpwHTML, L"<A", pos)) {
  1813. type = RTF_TAG_TYPE_STARTP;
  1814. } else if(StrCaseCompare(lpwHTML, L"</A", pos)) {
  1815. type = RTF_TAG_TYPE_STARTP;
  1816. } else if(StrCaseCompare(lpwHTML, L"<BR", pos)) {
  1817. type = RTF_TAG_TYPE_BR;
  1818. bPlainCRLF = true;
  1819. } else if(StrCaseCompare(lpwHTML, L"<PRE", pos)) {
  1820. type = RTF_TAG_TYPE_PRE;
  1821. } else if(StrCaseCompare(lpwHTML, L"</PRE", pos)) {
  1822. type = RTF_TAG_TYPE_PRE;
  1823. } else if(StrCaseCompare(lpwHTML, L"<FONT", pos)) {
  1824. type = RTF_TAG_TYPE_FONT;
  1825. } else if(StrCaseCompare(lpwHTML, L"</FONT", pos)) {
  1826. type = RTF_TAG_TYPE_FONT;
  1827. } else if(StrCaseCompare(lpwHTML, L"<META", pos)) {
  1828. type = RTF_TAG_TYPE_HEADER;
  1829. } else if(StrCaseCompare(lpwHTML, L"<LINK", pos)) {
  1830. type = RTF_TAG_TYPE_HEADER;
  1831. } else if (StrCaseCompare(lpwHTML, L"<H", pos) && iswdigit(lpwHTML[pos+2])) {
  1832. type = RTF_TAG_TYPE_HEADER;
  1833. } else if (StrCaseCompare(lpwHTML, L"</H", pos) && iswdigit(lpwHTML[pos+3])) {
  1834. type = RTF_TAG_TYPE_HEADER;
  1835. } else if(StrCaseCompare(lpwHTML, L"<TITLE", pos)) {
  1836. type = RTF_TAG_TYPE_TITLE;
  1837. } else if(StrCaseCompare(lpwHTML, L"</TITLE", pos)) {
  1838. type = RTF_TAG_TYPE_TITLE;
  1839. } else if(StrCaseCompare(lpwHTML, L"<PLAIN", pos)) {
  1840. type = RTF_TAG_TYPE_FONT;
  1841. } else if(StrCaseCompare(lpwHTML, L"</PLAIN", pos)) {
  1842. type = RTF_TAG_TYPE_FONT;
  1843. }
  1844. if (StrCaseCompare(lpwHTML, L"</", pos))
  1845. type |= RTF_FLAG_CLOSE;
  1846. }
  1847. // Set correct state flag if closing tag (RTF_IN*)
  1848. if(type & RTF_FLAG_CLOSE) {
  1849. switch(type & 0xF0) {
  1850. case RTF_TAG_TYPE_HEAD:
  1851. if(!stackTag.empty() && stackTag.top() == RTF_INHEAD)
  1852. stackTag.pop();
  1853. break;
  1854. case RTF_TAG_TYPE_BODY:
  1855. if(!stackTag.empty() && stackTag.top() == RTF_INBODY)
  1856. stackTag.pop();
  1857. break;
  1858. case RTF_TAG_TYPE_HTML:
  1859. if(!stackTag.empty() && stackTag.top() == RTF_INHTML)
  1860. stackTag.pop();
  1861. break;
  1862. default:
  1863. break;
  1864. }
  1865. }
  1866. // Process special tag input
  1867. if(lpwHTML[pos] == '<' && !inTag) {
  1868. if(StrCaseCompare(lpwHTML, L"<!--", pos))
  1869. ++ulCommentMode;
  1870. if(ulCommentMode == 0) {
  1871. if(StrCaseCompare(lpwHTML, L"<STYLE", pos))
  1872. ulStyleMode = 1;
  1873. else if(StrCaseCompare(lpwHTML, L"</STYLE", pos)) {
  1874. if(ulStyleMode == 3) {
  1875. // Close the style content tag
  1876. strRTF += "}";
  1877. }
  1878. ulStyleMode = 0;
  1879. } else if(StrCaseCompare(lpwHTML, L"<DIV", pos) || StrCaseCompare(lpwHTML, L"<P", pos)) {
  1880. ulParMode = 1;
  1881. } else if(StrCaseCompare(lpwHTML, L"</DIV", pos) || StrCaseCompare(lpwHTML, L"</P", pos)) {
  1882. ulParMode = 0;
  1883. }
  1884. }
  1885. if(ulCommentMode < 2 && ulStyleMode < 2) {
  1886. strRTF += "\r\n{\\*\\htmltag" + stringify((ulParMode == 2 ? RTF_FLAG_INPAR : 0) | tag | type | stackTag.top()) + " ";
  1887. inTag = true;
  1888. bFirstText = true;
  1889. if(ulCommentMode)
  1890. // Inside comment now
  1891. ++ulCommentMode;
  1892. }
  1893. }
  1894. // Do actual output
  1895. if(lpwHTML[pos] == '\r') {
  1896. // Ingore \r
  1897. } else if(lpwHTML[pos] == '\n') {
  1898. if(inTag || ulCommentMode || ulStyleMode)
  1899. strRTF += " ";
  1900. else
  1901. strRTF += "\r\n{\\*\\htmltag" + stringify((ulParMode == 2 ? RTF_FLAG_INPAR : 0) | stackTag.top()) + " \\par }";
  1902. } else if(lpwHTML[pos] == '\t') {
  1903. if(inTag || ulCommentMode || ulStyleMode)
  1904. strRTF += "\\tab ";
  1905. else
  1906. strRTF += "\r\n{\\*\\htmltag" + stringify((ulParMode == 2 ? RTF_FLAG_INPAR : 0) | stackTag.top()) + " \\tab }";
  1907. } else if(lpwHTML[pos] == '{') {
  1908. strRTF += "\\{";
  1909. } else if(lpwHTML[pos] == '}') {
  1910. strRTF += "\\}";
  1911. } else if(lpwHTML[pos] == '\\') {
  1912. strRTF += "\\\\";
  1913. } else if(lpwHTML[pos] > 127) {
  1914. // Unicode character
  1915. char hex[12];
  1916. snprintf(hex, 12, "\\u%hd ?", (signed short)lpwHTML[pos]);
  1917. strRTF += hex;
  1918. } else if(StrCaseCompare(lpwHTML, L"&nbsp;", pos)) {
  1919. if(inTag || ulCommentMode || ulStyleMode)
  1920. strRTF += "&nbsp;";
  1921. else
  1922. strRTF += "\r\n{\\*\\htmltag64}{\\*\\htmltag" + stringify((ulParMode == 2 ? RTF_FLAG_INPAR : 0) | RTF_TAG_TYPE_STARTP | stackTag.top()) + " &nbsp;}";
  1923. pos+=5;
  1924. } else if(!inTag && !ulCommentMode && !ulStyleMode && lpwHTML[pos] == '&' && CHtmlEntity::validateHtmlEntity(std::wstring(lpwHTML + pos, 10)) ) {
  1925. size_t semicolon = pos;
  1926. while (lpwHTML[semicolon] && lpwHTML[semicolon] != ';')
  1927. ++semicolon;
  1928. if (lpwHTML[semicolon]) {
  1929. std::wstring strEntity;
  1930. WCHAR c;
  1931. std::string strChar;
  1932. strEntity.assign(lpwHTML + pos+1, semicolon-pos-1);
  1933. c = CHtmlEntity::HtmlEntityToChar(strEntity);
  1934. if (c > 32 && c < 128)
  1935. strChar = c;
  1936. else {
  1937. // Unicode character
  1938. char hex[12];
  1939. snprintf(hex, 12, "\\u%hd ?", (signed short)c); // unicode char + ascii representation (see \ucN rtf command)
  1940. strChar = hex;
  1941. }
  1942. // both strChar and strEntity in output, unicode in rtf space, entity in html space
  1943. strRTF += std::string("\\htmlrtf ") + strChar + "\\htmlrtf0{\\*\\htmltag" +
  1944. stringify((ulParMode == 2 ? RTF_FLAG_INPAR : 0) | RTF_TAG_TYPE_STARTP | stackTag.top()) +
  1945. "&" + convert_to<string>(strEntity) + ";}";
  1946. pos += strEntity.size() + 2;
  1947. continue;
  1948. }
  1949. } else {
  1950. if (!inTag && bFirstText)
  1951. bFirstText = false;
  1952. strRTF += lpwHTML[pos];
  1953. }
  1954. // Do post-processing output
  1955. if(lpwHTML[pos] == '>' && (inTag || ulCommentMode)) {
  1956. if(!ulCommentMode && ulStyleMode < 2)
  1957. strRTF += "}";
  1958. if(pos > 2 && StrCaseCompare(lpwHTML, L"-->", pos-2) && ulCommentMode) {
  1959. --ulCommentMode;
  1960. if(ulCommentMode == 1) {
  1961. ulCommentMode = 0;
  1962. strRTF += "}";
  1963. }
  1964. }
  1965. if(pos > 6 && StrCaseCompare(lpwHTML, L"/STYLE>", pos-6) && ulStyleMode) {
  1966. ulStyleMode = 0;
  1967. strRTF += "}";
  1968. }
  1969. if(ulStyleMode == 1)
  1970. ++ulStyleMode;
  1971. if(ulParMode == 1)
  1972. ++ulParMode;
  1973. if(ulStyleMode == 2) {
  1974. // Output the style content as a tag
  1975. ulStyleMode = 3;
  1976. strRTF += "\r\n{\\*\\htmltag" + stringify(RTF_TAG_TYPE_UNK | stackTag.top()) + " ";
  1977. }
  1978. if (!ulStyleMode && !ulCommentMode)
  1979. // Normal text must have \*\htmltag64 to suppress <p> in the final html output
  1980. strRTF += "{\\*\\htmltag64}";
  1981. inTag = false;
  1982. if (bPlainCRLF && !ulCommentMode && !ulStyleMode)
  1983. // Add a plaintext newline if needed, but only for non-style and non-comment parts
  1984. strRTF += "\\htmlrtf \\line \\htmlrtf0 ";
  1985. }
  1986. // Next char
  1987. ++pos;
  1988. // Set correct state flag (RTF_IN*)
  1989. if(!(type & RTF_FLAG_CLOSE)) {
  1990. switch(type & 0xF0) {
  1991. case RTF_TAG_TYPE_HTML:
  1992. stackTag.push(RTF_INHTML);
  1993. break;
  1994. case RTF_TAG_TYPE_BODY:
  1995. stackTag.push(RTF_INBODY);
  1996. break;
  1997. case RTF_TAG_TYPE_HEAD:
  1998. stackTag.push(RTF_INHEAD);
  1999. break;
  2000. default:
  2001. break;
  2002. }
  2003. }
  2004. }
  2005. strRTF +="}\r\n";
  2006. return hrSuccess;
  2007. }
  2008. /**
  2009. * Convert html stream to rtf stream, using a codepage for the html input.
  2010. *
  2011. * We convert the HTML Stream from the codepage to wstring, because of
  2012. * the codepage of the html string, which can be any codepage.
  2013. *
  2014. * @param[in] html Stream to the HTML string, read only
  2015. * @param[in] rtf Stream to the RTF string, write only
  2016. * @param[in] ulCodepage codepage of the HTML input.
  2017. * @return MAPI error code
  2018. */
  2019. HRESULT Util::HrHtmlToRtf(IStream *html, IStream *rtf, unsigned int ulCodepage)
  2020. {
  2021. wstring wstrHTML;
  2022. std::string strRTF;
  2023. HRESULT hr = HrConvertStreamToWString(html, ulCodepage, &wstrHTML);
  2024. if(hr != hrSuccess)
  2025. return hr;
  2026. hr = HrHtmlToRtf(wstrHTML.c_str(), strRTF);
  2027. if(hr != hrSuccess)
  2028. return hr;
  2029. return rtf->Write(strRTF.c_str(), strRTF.size(), NULL);
  2030. }
  2031. /**
  2032. * Converts binary data into its hexidecimal string representation.
  2033. *
  2034. * @param[in] inLength number of bytes in input
  2035. * @param[in] input data buffer to convert
  2036. * @param[out] output MAPIAllocateBuffer/More allocated buffer containing the string
  2037. * @param[in] parent optional pointer for MAPIAllocateMore
  2038. *
  2039. * @return MAPI error code
  2040. */
  2041. HRESULT Util::bin2hex(ULONG inLength, const BYTE *input, char **output,
  2042. void *parent)
  2043. {
  2044. static const char digits[] = "0123456789ABCDEF";
  2045. char *buffer = NULL;
  2046. HRESULT hr;
  2047. ULONG i, j;
  2048. if (parent)
  2049. hr = MAPIAllocateMore(inLength*2+1, parent, (void**)&buffer);
  2050. else
  2051. hr = MAPIAllocateBuffer(inLength*2+1, (void**)&buffer);
  2052. if (hr != hrSuccess)
  2053. return hr;
  2054. for (i = 0, j = 0; i < inLength; ++i) {
  2055. buffer[j++] = digits[input[i]>>4];
  2056. buffer[j++] = digits[input[i]&0x0F];
  2057. }
  2058. buffer[j] = '\0';
  2059. *output = buffer;
  2060. return hrSuccess;
  2061. }
  2062. /**
  2063. * Converts a string containing hexidecimal numbers into binary
  2064. * data. And it adds a 0 at the end of the data.
  2065. *
  2066. * @todo, check usage of this function to see if the terminating 0 is
  2067. * really useful. should really be removed.
  2068. *
  2069. * @param[in] input string to convert
  2070. * @param[in] len length of the input (must be a multiple of 2)
  2071. * @param[out] outLength length of the output
  2072. * @param[out] output binary version of the input
  2073. * @param[in] parent optional pointer used for MAPIAllocateMore
  2074. *
  2075. * @return MAPI Error code
  2076. */
  2077. HRESULT Util::hex2bin(const char *input, size_t len, ULONG *outLength, LPBYTE *output, void *parent)
  2078. {
  2079. HRESULT hr;
  2080. LPBYTE buffer = NULL;
  2081. if (len % 2 != 0)
  2082. return MAPI_E_INVALID_PARAMETER;
  2083. if (parent)
  2084. hr = MAPIAllocateMore(len/2+1, parent, (void**)&buffer);
  2085. else
  2086. hr = MAPIAllocateBuffer(len/2+1, (void**)&buffer);
  2087. if (hr != hrSuccess)
  2088. goto exit;
  2089. hr = hex2bin(input, len, buffer);
  2090. if(hr != hrSuccess)
  2091. goto exit;
  2092. buffer[len/2] = '\0';
  2093. *outLength = len/2;
  2094. *output = buffer;
  2095. exit:
  2096. if (hr != hrSuccess && parent == nullptr)
  2097. MAPIFreeBuffer(buffer);
  2098. return hr;
  2099. }
  2100. /**
  2101. * Converts a string containing hexidecimal numbers into binary
  2102. * data.
  2103. *
  2104. * @param[in] input string to convert
  2105. * @param[in] len length of the input (must be a multiple of 2)
  2106. * @param[out] output binary version of the input, must be able to receive len/2 bytes
  2107. *
  2108. * @return MAPI Error code
  2109. */
  2110. HRESULT Util::hex2bin(const char *input, size_t len, LPBYTE output)
  2111. {
  2112. ULONG i, j;
  2113. if (len % 2 != 0)
  2114. return MAPI_E_INVALID_PARAMETER;
  2115. for (i = 0, j = 0; i < len; ++j) {
  2116. output[j] = x2b(input[i++]) << 4;
  2117. output[j] |= x2b(input[i++]);
  2118. }
  2119. return hrSuccess;
  2120. }
  2121. /**
  2122. * Return the original body property tag of a message, or PR_NULL when unknown.
  2123. *
  2124. * @param[in] lpBody Pointer to the SPropValue containing the PR_BODY property.
  2125. * @param[in] lpHtml Pointer to the SPropValue containing the PR_HTML property.
  2126. * @param[in] lpRtfCompressed Pointer to the SPropValue containing the PR_RTF_COMPRESSED property.
  2127. * @param[in] lpRtfInSync Pointer to the SPropValue containing the PR_RTF_IN_SYNC property.
  2128. * @param[in] ulFlags If MAPI_UNICODE is specified, the PR_BODY proptag
  2129. * will be the PT_UNICODE version. Otherwise the
  2130. * PT_STRING8 version is returned.
  2131. *
  2132. * @return
  2133. */
  2134. ULONG Util::GetBestBody(const SPropValue *lpBody, const SPropValue *lpHtml,
  2135. const SPropValue *lpRtfCompressed, const SPropValue *lpRtfInSync,
  2136. ULONG ulFlags)
  2137. {
  2138. /**
  2139. * In this function we try to determine the best body based on the combination of values and error values
  2140. * for PR_BODY, PR_HTML, PR_RTF_COMPRESSED and PR_RTF_IN_SYNC according to the rules as described in ECMessage.cpp.
  2141. * Some checks performed here seem redundant, but are actualy required to determine if the source provider
  2142. * implements this scheme as we expect it (Scalix doesn't always seem to do so).
  2143. */
  2144. const ULONG ulBodyTag = ((ulFlags & MAPI_UNICODE) ? PR_BODY_W : PR_BODY_A);
  2145. if (lpRtfInSync->ulPropTag != PR_RTF_IN_SYNC)
  2146. return PR_NULL;
  2147. if ((lpBody->ulPropTag == ulBodyTag || (PROP_TYPE(lpBody->ulPropTag) == PT_ERROR && lpBody->Value.err == MAPI_E_NOT_ENOUGH_MEMORY)) &&
  2148. (PROP_TYPE(lpHtml->ulPropTag) == PT_ERROR && lpHtml->Value.err == MAPI_E_NOT_FOUND) &&
  2149. (PROP_TYPE(lpRtfCompressed->ulPropTag) == PT_ERROR && lpRtfCompressed->Value.err == MAPI_E_NOT_FOUND))
  2150. return ulBodyTag;
  2151. if ((lpHtml->ulPropTag == PR_HTML || (PROP_TYPE(lpHtml->ulPropTag) == PT_ERROR && lpHtml->Value.err == MAPI_E_NOT_ENOUGH_MEMORY)) &&
  2152. (PROP_TYPE(lpBody->ulPropTag) == PT_ERROR && lpBody->Value.err == MAPI_E_NOT_ENOUGH_MEMORY) &&
  2153. (PROP_TYPE(lpRtfCompressed->ulPropTag) == PT_ERROR && lpRtfCompressed->Value.err == MAPI_E_NOT_ENOUGH_MEMORY) &&
  2154. lpRtfInSync->Value.b == FALSE)
  2155. return PR_HTML;
  2156. if ((lpRtfCompressed->ulPropTag == PR_RTF_COMPRESSED || (PROP_TYPE(lpRtfCompressed->ulPropTag) == PT_ERROR && lpRtfCompressed->Value.err == MAPI_E_NOT_ENOUGH_MEMORY)) &&
  2157. (PROP_TYPE(lpBody->ulPropTag) == PT_ERROR && lpBody->Value.err == MAPI_E_NOT_ENOUGH_MEMORY) &&
  2158. (PROP_TYPE(lpHtml->ulPropTag) == PT_ERROR && lpHtml->Value.err == MAPI_E_NOT_FOUND) &&
  2159. lpRtfInSync->Value.b == TRUE)
  2160. return PR_RTF_COMPRESSED;
  2161. return PR_NULL;
  2162. }
  2163. /**
  2164. * Return the original body property tag of a message, or PR_NULL when unknown.
  2165. *
  2166. * @param[in] lpPropObj The object to get the best body proptag from.
  2167. * @param[in] ulFlags If MAPI_UNICODE is specified, the PR_BODY proptag
  2168. * will be the PT_UNICODE version. Otherwise the
  2169. * PT_STRING8 version is returned.
  2170. *
  2171. * @return
  2172. */
  2173. ULONG Util::GetBestBody(IMAPIProp* lpPropObj, ULONG ulFlags)
  2174. {
  2175. HRESULT hr = hrSuccess;
  2176. SPropArrayPtr ptrBodies;
  2177. const ULONG ulBodyTag = ((ulFlags & MAPI_UNICODE) ? PR_BODY_W : PR_BODY_A);
  2178. SizedSPropTagArray (4, sBodyTags) = { 4, {
  2179. ulBodyTag,
  2180. PR_HTML,
  2181. PR_RTF_COMPRESSED,
  2182. PR_RTF_IN_SYNC
  2183. } };
  2184. ULONG cValues = 0;
  2185. hr = lpPropObj->GetProps(sBodyTags, 0, &cValues, &~ptrBodies);
  2186. if (FAILED(hr))
  2187. return PR_NULL;
  2188. return GetBestBody(&ptrBodies[0], &ptrBodies[1], &ptrBodies[2], &ptrBodies[3], ulFlags);
  2189. }
  2190. /**
  2191. * Return the original body property tag of a message, or PR_NULL when unknown.
  2192. *
  2193. * @param[in] lpPropArray The array of properties on which to base the result.
  2194. * This array must include PR_BODY, PR_HTML, PR_RTF_COMPRESSED
  2195. * and PR_RTF_IN_SYNC.
  2196. * @param[in] cValues The number of properties in lpPropArray.
  2197. * @param[in] ulFlags If MAPI_UNICODE is specified, the PR_BODY proptag
  2198. * will be the PT_UNICODE version. Otherwise the
  2199. * PT_STRING8 version is returned.
  2200. *
  2201. * @return
  2202. */
  2203. ULONG Util::GetBestBody(LPSPropValue lpPropArray, ULONG cValues, ULONG ulFlags)
  2204. {
  2205. auto lpBody = PCpropFindProp(lpPropArray, cValues, CHANGE_PROP_TYPE(PR_BODY, PT_UNSPECIFIED));
  2206. if (!lpBody)
  2207. return PR_NULL;
  2208. auto lpHtml = PCpropFindProp(lpPropArray, cValues, CHANGE_PROP_TYPE(PR_HTML, PT_UNSPECIFIED));
  2209. if (!lpHtml)
  2210. return PR_NULL;
  2211. auto lpRtfCompressed = PCpropFindProp(lpPropArray, cValues, CHANGE_PROP_TYPE(PR_RTF_COMPRESSED, PT_UNSPECIFIED));
  2212. if (!lpRtfCompressed)
  2213. return PR_NULL;
  2214. auto lpRtfInSync = PCpropFindProp(lpPropArray, cValues, CHANGE_PROP_TYPE(PR_RTF_IN_SYNC, PT_UNSPECIFIED));
  2215. if (!lpRtfInSync)
  2216. return PR_NULL;
  2217. return GetBestBody(lpBody, lpHtml, lpRtfCompressed, lpRtfInSync, ulFlags);
  2218. }
  2219. /**
  2220. * Check if a proptag specifies a body property. This is PR_BODY, PR_HTML
  2221. * or PR_RTF_COMPRESSED. If the type is set to error, it can still classified
  2222. * as a body proptag.
  2223. *
  2224. * @param[in] ulPropTag The proptag to check,
  2225. * @retval true if the proptag specified a body property.
  2226. * @retval false otherwise.
  2227. */
  2228. bool Util::IsBodyProp(ULONG ulPropTag)
  2229. {
  2230. switch (PROP_ID(ulPropTag)) {
  2231. case PROP_ID(PR_BODY):
  2232. case PROP_ID(PR_HTML):
  2233. case PROP_ID(PR_RTF_COMPRESSED):
  2234. return true;
  2235. default:
  2236. return false;
  2237. }
  2238. }
  2239. /**
  2240. * Find an interface IID in an array of interface definitions.
  2241. *
  2242. * @param[in] lpIID interface to find
  2243. * @param[in] ulIIDs number of entries in lpIIDs
  2244. * @param[in] lpIIDs array of interfaces
  2245. *
  2246. * @return MAPI error code
  2247. * @retval MAPI_E_NOT_FOUND interface not found in array
  2248. */
  2249. HRESULT Util::FindInterface(LPCIID lpIID, ULONG ulIIDs, LPCIID lpIIDs) {
  2250. HRESULT hr = MAPI_E_NOT_FOUND;
  2251. ULONG i;
  2252. if (!lpIIDs || !lpIID)
  2253. return MAPI_E_NOT_FOUND;
  2254. for (i = 0; i < ulIIDs; ++i) {
  2255. if (*lpIID == lpIIDs[i]) {
  2256. hr = hrSuccess;
  2257. break;
  2258. }
  2259. }
  2260. return hr;
  2261. }
  2262. /**
  2263. * Copy a complete stream to another.
  2264. *
  2265. * @param[in] lpSrc Input stream to copy
  2266. * @param[in] lpDest Stream to append data of lpSrc to
  2267. *
  2268. * @return MAPI error code
  2269. */
  2270. HRESULT Util::CopyStream(LPSTREAM lpSrc, LPSTREAM lpDest) {
  2271. ULARGE_INTEGER liRead = {{0}}, liWritten = {{0}};
  2272. STATSTG stStatus;
  2273. HRESULT hr = lpSrc->Stat(&stStatus, 0);
  2274. if (FAILED(hr))
  2275. return hr;
  2276. hr = lpSrc->CopyTo(lpDest, stStatus.cbSize, &liRead, &liWritten);
  2277. if (FAILED(hr))
  2278. return hr;
  2279. if (liRead.QuadPart != liWritten.QuadPart)
  2280. return MAPI_W_PARTIAL_COMPLETION;
  2281. return lpDest->Commit(0);
  2282. }
  2283. /**
  2284. * Copy all recipients from a source message to another message.
  2285. *
  2286. * @param[in] lpSrc Message containing recipients to copy
  2287. * @param[out] lpDest Message to add (append) all recipients to
  2288. *
  2289. * @return MAPI error code
  2290. */
  2291. HRESULT Util::CopyRecipients(LPMESSAGE lpSrc, LPMESSAGE lpDest) {
  2292. HRESULT hr;
  2293. object_ptr<IMAPITable> lpTable;
  2294. rowset_ptr lpRows;
  2295. memory_ptr<SPropTagArray> lpTableColumns;
  2296. ULONG ulRows = 0;
  2297. hr = lpSrc->GetRecipientTable(MAPI_UNICODE, &~lpTable);
  2298. if (hr != hrSuccess)
  2299. return hr;
  2300. hr = lpTable->QueryColumns(TBL_ALL_COLUMNS, &~lpTableColumns);
  2301. if (hr != hrSuccess)
  2302. return hr;
  2303. hr = lpTable->SetColumns(lpTableColumns, 0);
  2304. if (hr != hrSuccess)
  2305. return hr;
  2306. hr = lpTable->GetRowCount(0, &ulRows);
  2307. if (hr != hrSuccess)
  2308. return hr;
  2309. if (ulRows == 0) // Nothing to do!
  2310. return hrSuccess;
  2311. hr = lpTable->QueryRows(ulRows, 0, &~lpRows);
  2312. if (hr != hrSuccess)
  2313. return hr;
  2314. // LPADRLIST and LPSRowSet are binary compatible \o/
  2315. return lpDest->ModifyRecipients(MODRECIP_ADD,
  2316. reinterpret_cast<ADRLIST *>(lpRows.get()));
  2317. }
  2318. /**
  2319. * Copy a single-instance id to another object, if possible.
  2320. *
  2321. * @param lpSrc Source object (message or attachment)
  2322. * @param lpDst Destination object to have the same contents as source
  2323. *
  2324. * @return always hrSuccess
  2325. */
  2326. HRESULT Util::CopyInstanceIds(LPMAPIPROP lpSrc, LPMAPIPROP lpDst)
  2327. {
  2328. object_ptr<IECSingleInstance> lpSrcInstance, lpDstInstance;
  2329. ULONG cbInstanceID = 0;
  2330. memory_ptr<ENTRYID> lpInstanceID;
  2331. /*
  2332. * We are always going to return hrSuccess, if for some reason we can't copy the single instance,
  2333. * we always have the real data as fallback.
  2334. */
  2335. if (lpSrc->QueryInterface(IID_IECSingleInstance, &~lpSrcInstance) != hrSuccess)
  2336. return hrSuccess;
  2337. if (lpDst->QueryInterface(IID_IECSingleInstance, &~lpDstInstance) != hrSuccess)
  2338. return hrSuccess;
  2339. /*
  2340. * Transfer instance Id, if this succeeds we're in luck and we might not
  2341. * have to send the attachment to the server. Note that while SetSingleInstanceId()
  2342. * might succeed now, the attachment might be deleted on the server between
  2343. * SetSingleInstanceId() and SaveChanges(). In that case SaveChanges will fail
  2344. * and we will have to resend the attachment data.
  2345. */
  2346. if (lpSrcInstance->GetSingleInstanceId(&cbInstanceID, &~lpInstanceID) != hrSuccess)
  2347. return hrSuccess;
  2348. if (lpDstInstance->SetSingleInstanceId(cbInstanceID, lpInstanceID) != hrSuccess)
  2349. return hrSuccess;
  2350. return hrSuccess;
  2351. }
  2352. /**
  2353. * Copy all attachment properties from one attachment to another. The
  2354. * exclude property tag array is optional.
  2355. *
  2356. * @param[in] lpSrcAttach Attachment to copy data from
  2357. * @param[out] lpDstAttach Attachment to copy data to
  2358. * @param[in] lpExcludeProps Optional list of properties to not copy
  2359. *
  2360. * @return MAPI error code
  2361. */
  2362. HRESULT Util::CopyAttachmentProps(LPATTACH lpSrcAttach, LPATTACH lpDstAttach, LPSPropTagArray lpExcludeProps)
  2363. {
  2364. return Util::DoCopyTo(&IID_IAttachment, lpSrcAttach, 0, NULL,
  2365. lpExcludeProps, 0, NULL, &IID_IAttachment, lpDstAttach, 0, NULL);
  2366. }
  2367. /**
  2368. * Copy all attachments from one message to another.
  2369. *
  2370. * @param[in] lpSrc Source message to copy from
  2371. * @param[in] lpDest Message to copy attachments to
  2372. * @param[in] lpRestriction Optional restriction to apply before copying
  2373. * the attachments.
  2374. *
  2375. * @return MAPI error code
  2376. */
  2377. HRESULT Util::CopyAttachments(LPMESSAGE lpSrc, LPMESSAGE lpDest, LPSRestriction lpRestriction) {
  2378. HRESULT hr;
  2379. bool bPartial = false;
  2380. // table
  2381. object_ptr<IMAPITable> lpTable;
  2382. rowset_ptr lpRows;
  2383. memory_ptr<SPropTagArray> lpTableColumns;
  2384. ULONG ulRows = 0;
  2385. // attachments
  2386. memory_ptr<SPropValue> lpHasAttach;
  2387. ULONG ulAttachNr = 0;
  2388. hr = HrGetOneProp(lpSrc, PR_HASATTACH, &~lpHasAttach);
  2389. if (hr != hrSuccess)
  2390. return hrSuccess;
  2391. if (lpHasAttach->Value.b == FALSE)
  2392. return hrSuccess;
  2393. hr = lpSrc->GetAttachmentTable(MAPI_UNICODE, &~lpTable);
  2394. if (hr != hrSuccess)
  2395. return hr;
  2396. hr = lpTable->QueryColumns(TBL_ALL_COLUMNS, &~lpTableColumns);
  2397. if (hr != hrSuccess)
  2398. return hr;
  2399. hr = lpTable->SetColumns(lpTableColumns, 0);
  2400. if (hr != hrSuccess)
  2401. return hr;
  2402. if (lpRestriction) {
  2403. hr = lpTable->Restrict(lpRestriction, 0);
  2404. if (hr != hrSuccess)
  2405. return hr;
  2406. }
  2407. hr = lpTable->GetRowCount(0, &ulRows);
  2408. if (hr != hrSuccess)
  2409. return hr;
  2410. hr = lpTable->QueryRows(ulRows, 0, &~lpRows);
  2411. if (hr != hrSuccess)
  2412. return hr;
  2413. for (ULONG i = 0; i < lpRows->cRows; ++i) {
  2414. object_ptr<IAttach> lpDestAttach, lpSrcAttach;
  2415. auto lpAttachNum = PCpropFindProp(lpRows->aRow[i].lpProps, lpRows->aRow[i].cValues, PR_ATTACH_NUM);
  2416. if (!lpAttachNum) {
  2417. bPartial = true;
  2418. goto next_attach;
  2419. }
  2420. hr = lpSrc->OpenAttach(lpAttachNum->Value.ul, NULL, 0, &~lpSrcAttach);
  2421. if (hr != hrSuccess) {
  2422. bPartial = true;
  2423. goto next_attach;
  2424. }
  2425. hr = lpDest->CreateAttach(NULL, 0, &ulAttachNr, &~lpDestAttach);
  2426. if (hr != hrSuccess) {
  2427. bPartial = true;
  2428. goto next_attach;
  2429. }
  2430. hr = CopyAttachmentProps(lpSrcAttach, lpDestAttach);
  2431. if (hr != hrSuccess) {
  2432. bPartial = true;
  2433. goto next_attach;
  2434. }
  2435. /*
  2436. * Try making a single instance copy (without sending the attachment data to server).
  2437. * No error checking, we do not care if this fails, we still have all the data.
  2438. */
  2439. CopyInstanceIds(lpSrcAttach, lpDestAttach);
  2440. hr = lpDestAttach->SaveChanges(0);
  2441. if (hr != hrSuccess)
  2442. return hr;
  2443. next_attach:
  2444. ;
  2445. }
  2446. if (bPartial)
  2447. hr = MAPI_W_PARTIAL_COMPLETION;
  2448. return hr;
  2449. }
  2450. /**
  2451. * Copies all folders and contents from lpSrc to lpDest folder.
  2452. *
  2453. * Recursively copies contents from one folder to another. Location of
  2454. * lpSrc and lpDest does not matter, as long as there is read rights
  2455. * in lpSrc and write rights in lpDest.
  2456. *
  2457. * @param[in] lpSrc Source folder to copy
  2458. * @param[in] lpDest Source folder to copy
  2459. * @param[in] ulFlags See ISupport::DoCopyTo for valid flags
  2460. * @param[in] ulUIParam Unused in Linux.
  2461. * @param[in] lpProgress IMAPIProgress object. Unused in Linux.
  2462. *
  2463. * @return MAPI error code.
  2464. */
  2465. HRESULT Util::CopyHierarchy(LPMAPIFOLDER lpSrc, LPMAPIFOLDER lpDest, ULONG ulFlags, ULONG ulUIParam, LPMAPIPROGRESS lpProgress) {
  2466. HRESULT hr;
  2467. bool bPartial = false;
  2468. object_ptr<IMAPITable> lpTable;
  2469. static constexpr const SizedSPropTagArray(2, sptaName) =
  2470. {2, {PR_DISPLAY_NAME_W, PR_ENTRYID}};
  2471. object_ptr<IMAPIFolder> lpSrcParam, lpDestParam;
  2472. ULONG ulObj;
  2473. // sanity checks
  2474. if (lpSrc == nullptr || lpDest == nullptr)
  2475. return MAPI_E_INVALID_PARAMETER;
  2476. hr = lpSrc->QueryInterface(IID_IMAPIFolder, &~lpSrcParam);
  2477. if (hr != hrSuccess)
  2478. return hr;
  2479. hr = lpDest->QueryInterface(IID_IMAPIFolder, &~lpDestParam);
  2480. if (hr != hrSuccess)
  2481. return hr;
  2482. hr = lpSrc->GetHierarchyTable(MAPI_UNICODE, &~lpTable);
  2483. if (hr != hrSuccess)
  2484. return hr;
  2485. hr = lpTable->SetColumns(sptaName, 0);
  2486. if (hr != hrSuccess)
  2487. return hr;
  2488. while (true) {
  2489. object_ptr<IMAPIFolder> lpSrcFolder, lpDestFolder;
  2490. rowset_ptr lpRowSet;
  2491. hr = lpTable->QueryRows(1, 0, &~lpRowSet);
  2492. if (hr != hrSuccess)
  2493. return hr;
  2494. if (lpRowSet->cRows == 0)
  2495. break;
  2496. hr = lpSrc->OpenEntry(lpRowSet->aRow[0].lpProps[1].Value.bin.cb, reinterpret_cast<ENTRYID *>(lpRowSet->aRow[0].lpProps[1].Value.bin.lpb), &IID_IMAPIFolder, 0, &ulObj, &~lpSrcFolder);
  2497. if (hr != hrSuccess) {
  2498. bPartial = true;
  2499. continue;
  2500. }
  2501. hr = lpDest->CreateFolder(FOLDER_GENERIC, (LPTSTR)lpRowSet->aRow[0].lpProps[0].Value.lpszW, NULL, &IID_IMAPIFolder,
  2502. MAPI_UNICODE | (ulFlags & MAPI_NOREPLACE ? 0 : OPEN_IF_EXISTS), &~lpDestFolder);
  2503. if (hr != hrSuccess) {
  2504. bPartial = true;
  2505. continue;
  2506. }
  2507. hr = Util::DoCopyTo(&IID_IMAPIFolder, lpSrcFolder, 0, NULL, NULL, ulUIParam, lpProgress, &IID_IMAPIFolder, lpDestFolder, ulFlags, NULL);
  2508. if (FAILED(hr))
  2509. return hr;
  2510. else if (hr != hrSuccess) {
  2511. bPartial = true;
  2512. continue;
  2513. }
  2514. if (ulFlags & MAPI_MOVE)
  2515. lpSrc->DeleteFolder(lpRowSet->aRow[0].lpProps[1].Value.bin.cb, (LPENTRYID)lpRowSet->aRow[0].lpProps[1].Value.bin.lpb, 0, NULL, 0);
  2516. }
  2517. if (bPartial)
  2518. hr = MAPI_W_PARTIAL_COMPLETION;
  2519. return hr;
  2520. }
  2521. /**
  2522. * Copy all messages from a folder to another.
  2523. *
  2524. * @param[in] ulWhat 0 for normal messages, MAPI_ASSOCIATED for associated messages
  2525. * @param[in] lpSrc The source folder to copy messages from
  2526. * @param[out] lpDest The destination folder to copy messages in
  2527. * @param[in] ulFlags CopyTo flags, like MAPI_MOVE, or 0
  2528. * @param[in] ulUIParam Unused parameter passed to CopyTo functions
  2529. * @param[in] lpProgress Unused progress object
  2530. *
  2531. * @return MAPI error code
  2532. */
  2533. #define MAX_ROWS 50
  2534. HRESULT Util::CopyContents(ULONG ulWhat, LPMAPIFOLDER lpSrc, LPMAPIFOLDER lpDest, ULONG ulFlags, ULONG ulUIParam, LPMAPIPROGRESS lpProgress) {
  2535. HRESULT hr;
  2536. bool bPartial = false;
  2537. object_ptr<IMAPITable> lpTable;
  2538. static constexpr const SizedSPropTagArray(1, sptaEntryID) = {1, {PR_ENTRYID}};
  2539. ULONG ulObj;
  2540. memory_ptr<ENTRYLIST> lpDeleteEntries;
  2541. hr = lpSrc->GetContentsTable(MAPI_UNICODE | ulWhat, &~lpTable);
  2542. if (hr != hrSuccess)
  2543. return hr;
  2544. hr = lpTable->SetColumns(sptaEntryID, 0);
  2545. if (hr != hrSuccess)
  2546. return hr;
  2547. hr = MAPIAllocateBuffer(sizeof(ENTRYLIST), &~lpDeleteEntries);
  2548. if (hr != hrSuccess)
  2549. return hr;
  2550. hr = MAPIAllocateMore(sizeof(SBinary)*MAX_ROWS, lpDeleteEntries, (void**)&lpDeleteEntries->lpbin);
  2551. if (hr != hrSuccess)
  2552. return hr;
  2553. while (true) {
  2554. rowset_ptr lpRowSet;
  2555. hr = lpTable->QueryRows(MAX_ROWS, 0, &~lpRowSet);
  2556. if (hr != hrSuccess)
  2557. return hr;
  2558. if (lpRowSet->cRows == 0)
  2559. break;
  2560. lpDeleteEntries->cValues = 0;
  2561. for (ULONG i = 0; i < lpRowSet->cRows; ++i) {
  2562. object_ptr<IMessage> lpSrcMessage, lpDestMessage;
  2563. hr = lpSrc->OpenEntry(lpRowSet->aRow[i].lpProps[0].Value.bin.cb, reinterpret_cast<ENTRYID *>(lpRowSet->aRow[i].lpProps[0].Value.bin.lpb), &IID_IMessage, 0, &ulObj, &~lpSrcMessage);
  2564. if (hr != hrSuccess) {
  2565. bPartial = true;
  2566. continue;
  2567. }
  2568. hr = lpDest->CreateMessage(&IID_IMessage, ulWhat | MAPI_MODIFY, &~lpDestMessage);
  2569. if (hr != hrSuccess) {
  2570. bPartial = true;
  2571. continue;
  2572. }
  2573. hr = Util::DoCopyTo(&IID_IMessage, lpSrcMessage, 0, NULL, NULL, ulUIParam, lpProgress, &IID_IMessage, lpDestMessage, ulFlags, NULL);
  2574. if (FAILED(hr))
  2575. return hr;
  2576. else if (hr != hrSuccess) {
  2577. bPartial = true;
  2578. continue;
  2579. }
  2580. hr = lpDestMessage->SaveChanges(0);
  2581. if (hr != hrSuccess) {
  2582. bPartial = true;
  2583. } else if (ulFlags & MAPI_MOVE) {
  2584. lpDeleteEntries->lpbin[lpDeleteEntries->cValues].cb = lpRowSet->aRow[i].lpProps[0].Value.bin.cb;
  2585. lpDeleteEntries->lpbin[lpDeleteEntries->cValues].lpb = lpRowSet->aRow[i].lpProps[0].Value.bin.lpb;
  2586. ++lpDeleteEntries->cValues;
  2587. }
  2588. }
  2589. if (ulFlags & MAPI_MOVE && lpDeleteEntries->cValues > 0 &&
  2590. lpSrc->DeleteMessages(lpDeleteEntries, 0, NULL, 0) != hrSuccess)
  2591. bPartial = true;
  2592. }
  2593. if (bPartial)
  2594. hr = MAPI_W_PARTIAL_COMPLETION;
  2595. return hr;
  2596. }
  2597. /**
  2598. * Call OpenProperty on a property of an object to get the streamed
  2599. * version of that property. Will try to open with STGM_TRANSACTED,
  2600. * and disable this flag if an error was received.
  2601. *
  2602. * @param[in] ulPropType The type of the property to open.
  2603. * @param ulSrcPropTag The source property tag to open on the source object
  2604. * @param lpPropSrc The source object containing the property to open
  2605. * @param ulDestPropTag The destination property tag to open on the destination object
  2606. * @param lpPropDest The destination object where the property should be copied to
  2607. * @param lppSrcStream The source property as stream
  2608. * @param lppDestStream The destination property as stream
  2609. *
  2610. * @return MAPI error code
  2611. */
  2612. HRESULT Util::TryOpenProperty(ULONG ulPropType, ULONG ulSrcPropTag, LPMAPIPROP lpPropSrc, ULONG ulDestPropTag, LPMAPIPROP lpPropDest, LPSTREAM *lppSrcStream, LPSTREAM *lppDestStream) {
  2613. HRESULT hr;
  2614. object_ptr<IStream> lpSrc, lpDest;
  2615. hr = lpPropSrc->OpenProperty(PROP_TAG(ulPropType, PROP_ID(ulSrcPropTag)), &IID_IStream, 0, 0, &~lpSrc);
  2616. if (hr != hrSuccess)
  2617. return hr;
  2618. // some mapi functions/providers don't implement STGM_TRANSACTED, retry again without this flag
  2619. hr = lpPropDest->OpenProperty(PROP_TAG(ulPropType, PROP_ID(ulDestPropTag)), &IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~lpDest);
  2620. if (hr != hrSuccess)
  2621. hr = lpPropDest->OpenProperty(PROP_TAG(ulPropType, PROP_ID(ulDestPropTag)), &IID_IStream, STGM_WRITE, MAPI_CREATE | MAPI_MODIFY, &~lpDest);
  2622. if (hr != hrSuccess)
  2623. return hr;
  2624. *lppSrcStream = lpSrc.release();
  2625. *lppDestStream = lpDest.release();
  2626. return hrSuccess;
  2627. }
  2628. /**
  2629. * Adds a SPropProblem structure to an SPropProblemArray. If the
  2630. * problem array already contains data, it will first be copied to a
  2631. * new array, and one problem will be appended.
  2632. *
  2633. * @param[in] lpProblem The new problem to add to the array
  2634. * @param[in,out] lppProblems *lppProblems is NULL for a new array, otherwise a copy plus the addition is returned
  2635. *
  2636. * @return MAPI error code
  2637. */
  2638. HRESULT Util::AddProblemToArray(const SPropProblem *lpProblem,
  2639. SPropProblemArray **lppProblems)
  2640. {
  2641. HRESULT hr;
  2642. LPSPropProblemArray lpNewProblems = NULL;
  2643. LPSPropProblemArray lpOrigProblems = *lppProblems;
  2644. if (!lpOrigProblems) {
  2645. hr = MAPIAllocateBuffer(CbNewSPropProblemArray(1), (void**)&lpNewProblems);
  2646. if (hr != hrSuccess)
  2647. return hr;
  2648. lpNewProblems->cProblem = 1;
  2649. } else {
  2650. hr = MAPIAllocateBuffer(CbNewSPropProblemArray(lpOrigProblems->cProblem+1), (void**)&lpNewProblems);
  2651. if (hr != hrSuccess)
  2652. return hr;
  2653. lpNewProblems->cProblem = lpOrigProblems->cProblem +1;
  2654. memcpy(lpNewProblems->aProblem, lpOrigProblems->aProblem, sizeof(SPropProblem) * lpOrigProblems->cProblem);
  2655. MAPIFreeBuffer(lpOrigProblems);
  2656. }
  2657. memcpy(&lpNewProblems->aProblem[lpNewProblems->cProblem -1], lpProblem, sizeof(SPropProblem));
  2658. *lppProblems = lpNewProblems;
  2659. return hrSuccess;
  2660. }
  2661. /**
  2662. * Copies a MAPI object in-memory to a new MAPI object. Only
  2663. * IID_IStream or IID_IMAPIProp compatible interfaces can be copied.
  2664. *
  2665. * @param[in] lpSrcInterface The expected interface of lpSrcObj. Cannot be NULL.
  2666. * @param[in] lpSrcObj The source object to copy. Cannot be NULL.
  2667. * @param[in] ciidExclude Number of interfaces in rgiidExclude
  2668. * @param[in] rgiidExclude NULL or Interfaces to exclude in the copy, will return MAPI_E_INTERFACE_NOT_SUPPORTED if requested interface is found in the exclude list.
  2669. * @param[in] lpExcludeProps NULL or Array of properties to exclude in the copy process
  2670. * @param[in] ulUIParam Parameter for the callback in lpProgress, unused
  2671. * @param[in] lpProgress Unused progress object
  2672. * @param[in] lpDestInterface The expected interface of lpDstObj. Cannot be NULL.
  2673. * @param[out] lpDestObj The existing destination object. Cannot be NULL.
  2674. * @param[in] ulFlags can contain CopyTo flags, like MAPI_MOVE or MAPI_NOREPLACE
  2675. * @param[in] lppProblems Optional array containing problems encountered during the copy.
  2676. *
  2677. * @return MAPI error code
  2678. */
  2679. HRESULT Util::DoCopyTo(LPCIID lpSrcInterface, LPVOID lpSrcObj,
  2680. ULONG ciidExclude, LPCIID rgiidExclude, const SPropTagArray *lpExcludeProps,
  2681. ULONG ulUIParam, LPMAPIPROGRESS lpProgress, LPCIID lpDestInterface,
  2682. void *lpDestObj, ULONG ulFlags, SPropProblemArray **lppProblems)
  2683. {
  2684. HRESULT hr = hrSuccess;
  2685. LPUNKNOWN lpUnkSrc = (LPUNKNOWN)lpSrcObj, lpUnkDest = (LPUNKNOWN)lpDestObj;
  2686. bool bPartial = false;
  2687. // Properties that can never be copied (if you do this wrong, copying a message to a PST will fail)
  2688. SizedSPropTagArray(23, sExtraExcludes) = { 19, { PR_STORE_ENTRYID, PR_STORE_RECORD_KEY, PR_STORE_SUPPORT_MASK, PR_MAPPING_SIGNATURE,
  2689. PR_MDB_PROVIDER, PR_ACCESS_LEVEL, PR_RECORD_KEY, PR_HASATTACH, PR_NORMALIZED_SUBJECT,
  2690. PR_MESSAGE_SIZE, PR_DISPLAY_TO, PR_DISPLAY_CC, PR_DISPLAY_BCC, PR_ACCESS, PR_SUBJECT_PREFIX,
  2691. PR_OBJECT_TYPE, PR_ENTRYID, PR_PARENT_ENTRYID, PR_INTERNET_CONTENT,
  2692. PR_NULL, PR_NULL, PR_NULL, PR_NULL }};
  2693. object_ptr<IMAPIProp> lpPropSrc, lpPropDest;
  2694. memory_ptr<SPropTagArray> lpSPropTagArray;
  2695. if (!lpSrcInterface || !lpSrcObj || !lpDestInterface || !lpDestObj) {
  2696. hr = MAPI_E_INVALID_PARAMETER;
  2697. goto exit;
  2698. }
  2699. // source is "usually" the same as dest .. so we don't check (as ms mapi doesn't either)
  2700. hr = FindInterface(lpSrcInterface, ciidExclude, rgiidExclude);
  2701. if (hr == hrSuccess) {
  2702. hr = MAPI_E_INTERFACE_NOT_SUPPORTED;
  2703. goto exit;
  2704. }
  2705. hr = FindInterface(lpDestInterface, ciidExclude, rgiidExclude);
  2706. if (hr == hrSuccess) {
  2707. hr = MAPI_E_INTERFACE_NOT_SUPPORTED;
  2708. goto exit;
  2709. }
  2710. // first test IID_IStream .. the rest is IID_IMAPIProp compatible
  2711. if (*lpSrcInterface == IID_IStream) {
  2712. hr = FindInterface(&IID_IStream, ciidExclude, rgiidExclude);
  2713. if (hr == hrSuccess) {
  2714. hr = MAPI_E_INTERFACE_NOT_SUPPORTED;
  2715. goto exit;
  2716. }
  2717. if (*lpDestInterface != IID_IStream) {
  2718. hr = MAPI_E_INTERFACE_NOT_SUPPORTED;
  2719. goto exit;
  2720. }
  2721. hr = CopyStream((LPSTREAM)lpSrcObj, (LPSTREAM)lpDestObj);
  2722. goto exit;
  2723. }
  2724. hr = FindInterface(&IID_IMAPIProp, ciidExclude, rgiidExclude);
  2725. if (hr == hrSuccess) {
  2726. hr = MAPI_E_INTERFACE_NOT_SUPPORTED;
  2727. goto exit;
  2728. }
  2729. // end sanity checks
  2730. // check message, folder, attach, recipients, stream, mapitable, ... ?
  2731. if (*lpSrcInterface == IID_IMAPIFolder) {
  2732. // MS MAPI does not perform this check
  2733. if (*lpDestInterface != IID_IMAPIFolder) {
  2734. // on store, create folder and still go ?
  2735. hr = MAPI_E_INTERFACE_NOT_SUPPORTED;
  2736. goto exit;
  2737. }
  2738. if (!lpExcludeProps || Util::FindPropInArray(lpExcludeProps, PR_CONTAINER_CONTENTS) == -1) {
  2739. sExtraExcludes.aulPropTag[sExtraExcludes.cValues++] = PR_CONTAINER_CONTENTS;
  2740. hr = CopyContents(0, (LPMAPIFOLDER)lpSrcObj, (LPMAPIFOLDER)lpDestObj, ulFlags, ulUIParam, lpProgress);
  2741. if (hr != hrSuccess)
  2742. bPartial = true;
  2743. }
  2744. if (!lpExcludeProps || Util::FindPropInArray(lpExcludeProps, PR_FOLDER_ASSOCIATED_CONTENTS) == -1) {
  2745. sExtraExcludes.aulPropTag[sExtraExcludes.cValues++] = PR_FOLDER_ASSOCIATED_CONTENTS;
  2746. hr = CopyContents(MAPI_ASSOCIATED, (LPMAPIFOLDER)lpSrcObj, (LPMAPIFOLDER)lpDestObj, ulFlags, ulUIParam, lpProgress);
  2747. if (hr != hrSuccess)
  2748. bPartial = true;
  2749. }
  2750. if (!lpExcludeProps || Util::FindPropInArray(lpExcludeProps, PR_CONTAINER_HIERARCHY) == -1) {
  2751. // add to lpExcludeProps so CopyProps ignores them
  2752. sExtraExcludes.aulPropTag[sExtraExcludes.cValues++] = PR_CONTAINER_HIERARCHY;
  2753. hr = CopyHierarchy((LPMAPIFOLDER)lpSrcObj, (LPMAPIFOLDER)lpDestObj, ulFlags, ulUIParam, lpProgress);
  2754. if (hr != hrSuccess)
  2755. bPartial = true;
  2756. }
  2757. } else if (*lpSrcInterface == IID_IMessage) {
  2758. // recipients & attachments
  2759. // this is done in CopyProps ()
  2760. } else if (*lpSrcInterface == IID_IAttachment) {
  2761. // data stream
  2762. // this is done in CopyProps ()
  2763. } else if (*lpSrcInterface == IID_IMAPIContainer || *lpSrcInterface == IID_IMAPIProp) {
  2764. // props only
  2765. // this is done in CopyProps ()
  2766. } else if (*lpSrcInterface == IID_IMailUser || *lpSrcInterface == IID_IDistList) {
  2767. // in one if() ?
  2768. // this is done in CopyProps () ???
  2769. // what else besides props ???
  2770. } else {
  2771. // stores, ... ?
  2772. hr = MAPI_E_INTERFACE_NOT_SUPPORTED;
  2773. goto exit;
  2774. }
  2775. // we have a IMAPIProp compatible interface here, and we don't want to crash
  2776. hr = QueryInterfaceMapiPropOrValidFallback(lpUnkSrc, lpSrcInterface, &~lpPropSrc);
  2777. if (hr != hrSuccess)
  2778. goto exit;
  2779. hr = QueryInterfaceMapiPropOrValidFallback(lpUnkDest, lpDestInterface, &~lpPropDest);
  2780. if (hr != hrSuccess)
  2781. goto exit;
  2782. if (!FHasHTML(lpPropDest))
  2783. sExtraExcludes.aulPropTag[sExtraExcludes.cValues++] = PR_HTML;
  2784. hr = lpPropSrc->GetPropList(MAPI_UNICODE, &~lpSPropTagArray);
  2785. if (FAILED(hr))
  2786. goto exit;
  2787. // filter excludes
  2788. if (lpExcludeProps || sExtraExcludes.cValues != 0) {
  2789. for (ULONG i = 0; i < lpSPropTagArray->cValues; ++i) {
  2790. if (lpExcludeProps && Util::FindPropInArray(lpExcludeProps, CHANGE_PROP_TYPE(lpSPropTagArray->aulPropTag[i], PT_UNSPECIFIED)) != -1)
  2791. lpSPropTagArray->aulPropTag[i] = PR_NULL;
  2792. else if (Util::FindPropInArray(sExtraExcludes, CHANGE_PROP_TYPE(lpSPropTagArray->aulPropTag[i], PT_UNSPECIFIED)) != -1)
  2793. lpSPropTagArray->aulPropTag[i] = PR_NULL;
  2794. }
  2795. }
  2796. // Force some extra properties
  2797. if (*lpSrcInterface == IID_IMessage) {
  2798. bool bAddAttach = false;
  2799. bool bAddRecip = false;
  2800. if (Util::FindPropInArray(lpExcludeProps, PR_MESSAGE_ATTACHMENTS) == -1 && // not in exclude
  2801. Util::FindPropInArray(lpSPropTagArray, PR_MESSAGE_ATTACHMENTS) == -1) // not yet in props to copy
  2802. bAddAttach = true;
  2803. if (Util::FindPropInArray(lpExcludeProps, PR_MESSAGE_RECIPIENTS) == -1 && // not in exclude
  2804. Util::FindPropInArray(lpSPropTagArray, PR_MESSAGE_RECIPIENTS) == -1) // not yet in props to copy
  2805. bAddRecip = true;
  2806. if (bAddAttach || bAddRecip) {
  2807. memory_ptr<SPropTagArray> lpTempSPropTagArray;
  2808. ULONG ulNewPropCount = lpSPropTagArray->cValues + (bAddAttach ? (bAddRecip ? 2 : 1) : 1);
  2809. hr = MAPIAllocateBuffer(CbNewSPropTagArray(ulNewPropCount), &~lpTempSPropTagArray);
  2810. if (hr != hrSuccess)
  2811. goto exit;
  2812. memcpy(lpTempSPropTagArray->aulPropTag, lpSPropTagArray->aulPropTag, lpSPropTagArray->cValues * sizeof *lpSPropTagArray->aulPropTag);
  2813. if (bAddAttach)
  2814. lpTempSPropTagArray->aulPropTag[ulNewPropCount - (bAddRecip ? 2 : 1)] = PR_MESSAGE_ATTACHMENTS;
  2815. if (bAddRecip)
  2816. lpTempSPropTagArray->aulPropTag[ulNewPropCount - 1] = PR_MESSAGE_RECIPIENTS;
  2817. lpTempSPropTagArray->cValues = ulNewPropCount;
  2818. std::swap(lpTempSPropTagArray, lpSPropTagArray);
  2819. }
  2820. }
  2821. // this is input for CopyProps
  2822. hr = Util::DoCopyProps(lpSrcInterface, lpSrcObj, lpSPropTagArray, ulUIParam, lpProgress, lpDestInterface, lpDestObj, 0, lppProblems);
  2823. if (hr != hrSuccess)
  2824. goto exit;
  2825. // TODO: mapi move, delete OPEN message ???
  2826. exit:
  2827. // Partial warning when data was copied.
  2828. if (bPartial)
  2829. hr = MAPI_W_PARTIAL_COMPLETION;
  2830. return hr;
  2831. }
  2832. /**
  2833. * Check if the interface is a valid IMAPIProp interface
  2834. *
  2835. * @param[in] lpInterface Pointer to an interface GUID
  2836. *
  2837. * @retval MAPI_E_INTERFACE_NOT_SUPPORTED Interface not supported
  2838. * @retval S_OK Interface supported
  2839. */
  2840. HRESULT Util::ValidMapiPropInterface(LPCIID lpInterface)
  2841. {
  2842. if (!lpInterface)
  2843. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  2844. if (*lpInterface == IID_IAttachment ||
  2845. *lpInterface == IID_IMAPIProp ||
  2846. *lpInterface == IID_IProfSect ||
  2847. *lpInterface == IID_IMsgStore ||
  2848. *lpInterface == IID_IMessage ||
  2849. *lpInterface == IID_IAddrBook ||
  2850. *lpInterface == IID_IMailUser ||
  2851. *lpInterface == IID_IMAPIContainer ||
  2852. *lpInterface == IID_IMAPIFolder ||
  2853. *lpInterface == IID_IABContainer ||
  2854. *lpInterface == IID_IDistList)
  2855. return S_OK;
  2856. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  2857. }
  2858. /**
  2859. * Queryinterface IMAPIProp or a supported fallback interface
  2860. *
  2861. * @param[in] lpInObj Pointer to an IUnknown supported interface
  2862. * @param[in] lpInterface Pointer to an interface GUID
  2863. * @param[out] lppOutObj Pointer to a pointer which support a IMAPIProp interface.
  2864. *
  2865. * @retval MAPI_E_INTERFACE_NOT_SUPPORTED Interface not supported
  2866. * @retval S_OK Interface supported
  2867. */
  2868. HRESULT Util::QueryInterfaceMapiPropOrValidFallback(LPUNKNOWN lpInObj, LPCIID lpInterface, LPUNKNOWN *lppOutObj)
  2869. {
  2870. if (lpInObj == NULL || lppOutObj == NULL)
  2871. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  2872. HRESULT hr = lpInObj->QueryInterface(IID_IMAPIProp,
  2873. reinterpret_cast<void **>(lppOutObj));
  2874. if (hr == hrSuccess)
  2875. return hr;
  2876. hr = ValidMapiPropInterface(lpInterface);
  2877. if (hr != hrSuccess)
  2878. return hr;
  2879. return lpInObj->QueryInterface(*lpInterface, reinterpret_cast<void **>(lppOutObj));
  2880. }
  2881. /**
  2882. * Copy properties of one MAPI object to another. Only IID_IMAPIProp
  2883. * compatible objects are supported.
  2884. *
  2885. * @param[in] lpSrcInterface The expected interface of lpSrcObj. Cannot be NULL.
  2886. * @param[in] lpSrcObj The source object to copy. Cannot be NULL.
  2887. * @param[in] lpIncludeProps List of properties to copy, or NULL for all properties.
  2888. * @param[in] ulUIParam Parameter for the callback in lpProgress, unused
  2889. * @param[in] lpProgress Unused progress object
  2890. * @param[in] lpDestInterface The expected interface of lpDstObj. Cannot be NULL.
  2891. * @param[out] lpDestObj The existing destination object. Cannot be NULL.
  2892. * @param[in] ulFlags can contain CopyTo flags, like MAPI_MOVE or MAPI_NOREPLACE
  2893. * @param[in] lppProblems Optional array containing problems encountered during the copy.
  2894. *
  2895. * @return MAPI error code
  2896. */
  2897. HRESULT Util::DoCopyProps(LPCIID lpSrcInterface, void *lpSrcObj,
  2898. const SPropTagArray *inclprop, ULONG ulUIParam, LPMAPIPROGRESS lpProgress,
  2899. LPCIID lpDestInterface, void *lpDestObj, ULONG ulFlags,
  2900. SPropProblemArray **lppProblems)
  2901. {
  2902. HRESULT hr = hrSuccess;
  2903. LPUNKNOWN lpUnkSrc = (LPUNKNOWN)lpSrcObj, lpUnkDest = (LPUNKNOWN)lpDestObj;
  2904. object_ptr<IECUnknown> lpKopano;
  2905. memory_ptr<SPropValue> lpZObj, lpProps;
  2906. bool bPartial = false;
  2907. object_ptr<IMAPIProp> lpSrcProp, lpDestProp;
  2908. ULONG cValues = 0;
  2909. memory_ptr<SPropTagArray> lpsDestPropArray;
  2910. memory_ptr<SPropProblemArray> lpProblems;
  2911. // named props
  2912. ULONG cNames = 0;
  2913. memory_ptr<SPropTagArray> lpIncludeProps;
  2914. memory_ptr<SPropTagArray> lpsSrcNameTagArray, lpsDestNameTagArray;
  2915. memory_ptr<SPropTagArray> lpsDestTagArray;
  2916. memory_ptr<MAPINAMEID *> lppNames;
  2917. // attachments
  2918. memory_ptr<SPropValue> lpAttachMethod;
  2919. LONG ulIdCPID;
  2920. LONG ulIdRTF;
  2921. LONG ulIdHTML;
  2922. LONG ulIdBODY;
  2923. ULONG ulBodyProp = PR_BODY;
  2924. if (lpSrcInterface == nullptr || lpDestInterface == nullptr ||
  2925. lpSrcObj == nullptr || lpDestObj == nullptr ||
  2926. inclprop == nullptr) {
  2927. hr = MAPI_E_INVALID_PARAMETER;
  2928. goto exit;
  2929. }
  2930. // q-i src and dest to check if IID_IMAPIProp is present
  2931. hr = QueryInterfaceMapiPropOrValidFallback(lpUnkSrc, lpSrcInterface, &~lpSrcProp);
  2932. if (hr != hrSuccess)
  2933. goto exit;
  2934. hr = QueryInterfaceMapiPropOrValidFallback(lpUnkDest, lpDestInterface, &~lpDestProp);
  2935. if (hr != hrSuccess)
  2936. goto exit;
  2937. // take some shortcuts if we're dealing with a Kopano message destination
  2938. if (HrGetOneProp(lpDestProp, PR_EC_OBJECT, &~lpZObj) == hrSuccess &&
  2939. lpZObj->Value.lpszA != NULL)
  2940. reinterpret_cast<IECUnknown *>(lpZObj->Value.lpszA)->QueryInterface(IID_ECMessage, &~lpKopano);
  2941. /* remember which props not to copy */
  2942. hr = MAPIAllocateBuffer(CbNewSPropTagArray(inclprop->cValues), &~lpIncludeProps);
  2943. if (hr != hrSuccess)
  2944. return hr;
  2945. memcpy(lpIncludeProps, inclprop, CbNewSPropTagArray(inclprop->cValues));
  2946. if (ulFlags & MAPI_NOREPLACE) {
  2947. hr = lpDestProp->GetPropList(MAPI_UNICODE, &~lpsDestPropArray);
  2948. if (hr != hrSuccess)
  2949. goto exit;
  2950. for (ULONG i = 0; i < lpIncludeProps->cValues; ++i) {
  2951. if (Util::FindPropInArray(lpsDestPropArray, lpIncludeProps->aulPropTag[i]) == -1)
  2952. continue;
  2953. // hr = MAPI_E_COLLISION;
  2954. // goto exit;
  2955. // MSDN says collision, MS MAPI ignores these properties.
  2956. lpIncludeProps->aulPropTag[i] = PR_NULL;
  2957. }
  2958. }
  2959. if (lpKopano) {
  2960. // Use only one body text property, RTF, HTML or BODY when we're copying to another Kopano message.
  2961. ulIdRTF = Util::FindPropInArray(lpIncludeProps, PR_RTF_COMPRESSED);
  2962. ulIdHTML = Util::FindPropInArray(lpIncludeProps, PR_HTML);
  2963. ulIdBODY = Util::FindPropInArray(lpIncludeProps, PR_BODY_W);
  2964. // find out the original body type, and only copy that version
  2965. ulBodyProp = GetBestBody(lpSrcProp, fMapiUnicode);
  2966. if (ulBodyProp == PR_BODY && ulIdBODY != -1) {
  2967. // discard html and rtf
  2968. if(ulIdHTML != -1)
  2969. lpIncludeProps->aulPropTag[ulIdHTML] = PR_NULL;
  2970. if(ulIdRTF != -1)
  2971. lpIncludeProps->aulPropTag[ulIdRTF] = PR_NULL;
  2972. } else if (ulBodyProp == PR_HTML && ulIdHTML != -1) {
  2973. // discard plain and rtf
  2974. if(ulIdBODY != -1)
  2975. lpIncludeProps->aulPropTag[ulIdBODY] = PR_NULL;
  2976. if(ulIdRTF != -1)
  2977. lpIncludeProps->aulPropTag[ulIdRTF] = PR_NULL;
  2978. } else if (ulBodyProp == PR_RTF_COMPRESSED && ulIdRTF != -1) {
  2979. // discard plain and html
  2980. if(ulIdHTML != -1)
  2981. lpIncludeProps->aulPropTag[ulIdHTML] = PR_NULL;
  2982. if(ulIdBODY != -1)
  2983. lpIncludeProps->aulPropTag[ulIdBODY] = PR_NULL;
  2984. }
  2985. }
  2986. for (ULONG i = 0; i < lpIncludeProps->cValues; ++i) {
  2987. bool isProblem = false;
  2988. // TODO: ?
  2989. // for all PT_OBJECT properties on IMAPIProp, MS MAPI tries:
  2990. // IID_IMessage, IID_IStreamDocfile, IID_IStorage
  2991. if (PROP_TYPE(lpIncludeProps->aulPropTag[i]) != PT_OBJECT &&
  2992. PROP_ID(lpIncludeProps->aulPropTag[i]) != PROP_ID(PR_ATTACH_DATA_BIN))
  2993. continue;
  2994. // if IMessage: PR_MESSAGE_RECIPIENTS, PR_MESSAGE_ATTACHMENTS
  2995. if (*lpSrcInterface == IID_IMessage) {
  2996. if (lpIncludeProps->aulPropTag[i] == PR_MESSAGE_RECIPIENTS)
  2997. // TODO: add ulFlags, and check for MAPI_NOREPLACE
  2998. hr = Util::CopyRecipients((LPMESSAGE)lpSrcObj, (LPMESSAGE)lpDestObj);
  2999. else if (lpIncludeProps->aulPropTag[i] == PR_MESSAGE_ATTACHMENTS)
  3000. // TODO: add ulFlags, and check for MAPI_NOREPLACE
  3001. hr = Util::CopyAttachments((LPMESSAGE)lpSrcObj, (LPMESSAGE)lpDestObj, NULL);
  3002. else
  3003. hr = MAPI_E_INTERFACE_NOT_SUPPORTED;
  3004. if (hr != hrSuccess) {
  3005. isProblem = true;
  3006. goto next_include_check;
  3007. }
  3008. } else if (*lpSrcInterface == IID_IMAPIFolder) {
  3009. // MS MAPI skips these in CopyProps(), for unknown reasons
  3010. if (lpIncludeProps->aulPropTag[i] == PR_CONTAINER_CONTENTS ||
  3011. lpIncludeProps->aulPropTag[i] == PR_CONTAINER_HIERARCHY ||
  3012. lpIncludeProps->aulPropTag[i] == PR_FOLDER_ASSOCIATED_CONTENTS) {
  3013. lpIncludeProps->aulPropTag[i] = PR_NULL;
  3014. } else {
  3015. isProblem = true;
  3016. }
  3017. } else if (*lpSrcInterface == IID_IAttachment) {
  3018. object_ptr<IStream> lpSrcStream, lpDestStream;
  3019. object_ptr<IMessage> lpSrcMessage, lpDestMessage;
  3020. ULONG ulAttachMethod;
  3021. // In attachments, IID_IMessage can be present! for PR_ATTACH_DATA_OBJ
  3022. // find method and copy this PT_OBJECT
  3023. if (HrGetOneProp(lpSrcProp, PR_ATTACH_METHOD, &~lpAttachMethod) != hrSuccess)
  3024. ulAttachMethod = ATTACH_BY_VALUE;
  3025. else
  3026. ulAttachMethod = lpAttachMethod->Value.ul;
  3027. switch (ulAttachMethod) {
  3028. case ATTACH_BY_VALUE:
  3029. case ATTACH_OLE:
  3030. // stream
  3031. // Not being able to open the source message is not an error: it may just not be there
  3032. if (((LPATTACH)lpSrcObj)->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, 0, 0, &~lpSrcStream) == hrSuccess) {
  3033. // While dragging and dropping, Outlook 2007 (atleast) returns an internal MAPI object to CopyTo as destination
  3034. // The internal MAPI object is unable to make a stream STGM_TRANSACTED, so we retry the action without that flag
  3035. // to get the stream without the transaction feature.
  3036. hr = ((LPATTACH)lpDestObj)->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~lpDestStream);
  3037. if (hr != hrSuccess)
  3038. hr = ((LPATTACH)lpDestObj)->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, STGM_WRITE, MAPI_CREATE | MAPI_MODIFY, &~lpDestStream);
  3039. if (hr != hrSuccess) {
  3040. isProblem = true;
  3041. goto next_include_check;
  3042. }
  3043. hr = Util::CopyStream(lpSrcStream, lpDestStream);
  3044. if (hr != hrSuccess) {
  3045. isProblem = true;
  3046. goto next_include_check;
  3047. }
  3048. } else if(lpAttachMethod->Value.ul == ATTACH_OLE &&
  3049. ((LPATTACH)lpSrcObj)->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IStream, 0, 0, &~lpSrcStream) == hrSuccess) {
  3050. // OLE 2.0 must be open with PR_ATTACH_DATA_OBJ
  3051. hr = ((LPATTACH)lpDestObj)->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~lpDestStream);
  3052. if (hr == E_FAIL)
  3053. hr = ((LPATTACH)lpDestObj)->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IStream, STGM_WRITE, MAPI_CREATE | MAPI_MODIFY, &~lpDestStream);
  3054. if (hr != hrSuccess) {
  3055. isProblem = true;
  3056. goto next_include_check;
  3057. }
  3058. hr = Util::CopyStream(lpSrcStream, lpDestStream);
  3059. if (hr != hrSuccess) {
  3060. isProblem = true;
  3061. goto next_include_check;
  3062. }
  3063. }
  3064. break;
  3065. case ATTACH_EMBEDDED_MSG:
  3066. // message
  3067. if (((LPATTACH)lpSrcObj)->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, 0, &~lpSrcMessage) == hrSuccess) {
  3068. // Not being able to open the source message is not an error: it may just not be there
  3069. hr = ((LPATTACH)lpDestObj)->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY, &~lpDestMessage);
  3070. if (hr != hrSuccess) {
  3071. isProblem = true;
  3072. goto next_include_check;
  3073. }
  3074. hr = Util::DoCopyTo(&IID_IMessage, lpSrcMessage, 0, NULL, NULL, ulUIParam, lpProgress, &IID_IMessage, lpDestMessage, 0, NULL);
  3075. if (hr != hrSuccess) {
  3076. isProblem = true;
  3077. goto next_include_check;
  3078. }
  3079. hr = lpDestMessage->SaveChanges(0);
  3080. if (hr != hrSuccess) {
  3081. isProblem = true;
  3082. goto next_include_check;
  3083. }
  3084. }
  3085. break;
  3086. default:
  3087. // OLE objects?
  3088. isProblem = true;
  3089. break;
  3090. };
  3091. } else {
  3092. isProblem = true;
  3093. }
  3094. // TODO: try the 3 MSMAPI interfaces (message, stream, storage) if unhandled?
  3095. next_include_check:
  3096. if (isProblem) {
  3097. SPropProblem sProblem;
  3098. bPartial = true;
  3099. sProblem.ulIndex = i;
  3100. sProblem.ulPropTag = lpIncludeProps->aulPropTag[i];
  3101. sProblem.scode = MAPI_E_INTERFACE_NOT_SUPPORTED; // hr?
  3102. hr = AddProblemToArray(&sProblem, &+lpProblems);
  3103. if (hr != hrSuccess)
  3104. goto exit;
  3105. }
  3106. // skip this prop for the final SetProps()
  3107. lpIncludeProps->aulPropTag[i] = PR_NULL;
  3108. }
  3109. hr = lpSrcProp->GetProps(lpIncludeProps, 0, &cValues, &~lpProps);
  3110. if (FAILED(hr))
  3111. goto exit;
  3112. // make map for destination property tags, because named IDs may differ in src and dst
  3113. hr = MAPIAllocateBuffer(CbNewSPropTagArray(cValues), &~lpsDestTagArray);
  3114. if (hr != hrSuccess)
  3115. goto exit;
  3116. // get named props
  3117. for (ULONG i = 0; i < cValues; ++i) {
  3118. lpsDestTagArray->aulPropTag[i] = lpProps[i].ulPropTag;
  3119. if (PROP_ID(lpProps[i].ulPropTag) >= 0x8000)
  3120. ++cNames;
  3121. }
  3122. if (cNames) {
  3123. hr = MAPIAllocateBuffer(CbNewSPropTagArray(cNames), &~lpsSrcNameTagArray);
  3124. if (hr != hrSuccess)
  3125. goto exit;
  3126. lpsSrcNameTagArray->cValues = cNames;
  3127. cNames = 0;
  3128. for (ULONG i = 0; i < cValues; ++i)
  3129. if (PROP_ID(lpProps[i].ulPropTag) >= 0x8000)
  3130. lpsSrcNameTagArray->aulPropTag[cNames++] = lpProps[i].ulPropTag;
  3131. // ignore warnings on unknown named properties, but don't copy those either (see PT_ERROR below)
  3132. hr = lpSrcProp->GetNamesFromIDs(&+lpsSrcNameTagArray, NULL, 0, &cNames, &~lppNames);
  3133. if (FAILED(hr))
  3134. goto exit;
  3135. hr = lpDestProp->GetIDsFromNames(cNames, lppNames, MAPI_CREATE, &~lpsDestNameTagArray);
  3136. if (FAILED(hr))
  3137. goto exit;
  3138. // make new lookup map for lpProps[] -> lpsDestNameTag[]
  3139. for (ULONG i = 0, j = 0; i < cValues && j < cNames; ++i) {
  3140. if (PROP_ID(lpProps[i].ulPropTag) != PROP_ID(lpsSrcNameTagArray->aulPropTag[j]))
  3141. continue;
  3142. if (PROP_TYPE(lpsDestNameTagArray->aulPropTag[j]) != PT_ERROR)
  3143. // replace with new proptag, so we can open the correct property
  3144. lpsDestTagArray->aulPropTag[i] = PROP_TAG(PROP_TYPE(lpProps[i].ulPropTag), PROP_ID(lpsDestNameTagArray->aulPropTag[j]));
  3145. else
  3146. // leave on PT_ERROR, so we don't copy the property
  3147. lpsDestTagArray->aulPropTag[i] = PROP_TAG(PT_ERROR, PROP_ID(lpsDestNameTagArray->aulPropTag[j]));
  3148. // don't even return a warning because although not all data could be copied
  3149. ++j;
  3150. }
  3151. }
  3152. // before we copy bodies possibly as streams, find the PR_INTERNET_CPID, which is required for correct high-char translations.
  3153. ulIdCPID = Util::FindPropInArray(lpIncludeProps, PR_INTERNET_CPID);
  3154. if (ulIdCPID != -1) {
  3155. hr = lpDestProp->SetProps(1, &lpProps[ulIdCPID], NULL);
  3156. if (FAILED(hr))
  3157. goto exit;
  3158. }
  3159. // find all MAPI_E_NOT_ENOUGH_MEMORY errors
  3160. for (ULONG i = 0; i < cValues; ++i) {
  3161. bool err = false;
  3162. if(PROP_TYPE(lpProps[i].ulPropTag) == PT_ERROR)
  3163. err = lpProps[i].Value.err == MAPI_E_NOT_ENOUGH_MEMORY ||
  3164. PROP_ID(lpProps[i].ulPropTag) == PROP_ID(PR_BODY) ||
  3165. PROP_ID(lpProps[i].ulPropTag) == PROP_ID(PR_HTML) ||
  3166. PROP_ID(lpProps[i].ulPropTag) == PROP_ID(PR_RTF_COMPRESSED);
  3167. if (!err)
  3168. continue;
  3169. assert(PROP_ID(lpIncludeProps->aulPropTag[i]) == PROP_ID(lpProps[i].ulPropTag));
  3170. object_ptr<IStream> lpSrcStream, lpDestStream;
  3171. hr = Util::TryOpenProperty(PROP_TYPE(lpIncludeProps->aulPropTag[i]), lpProps[i].ulPropTag, lpSrcProp, lpsDestTagArray->aulPropTag[i], lpDestProp, &~lpSrcStream, &~lpDestStream);
  3172. if (hr != hrSuccess) {
  3173. // TODO: check, partial or problemarray?
  3174. // when the prop was not found (body property), it actually wasn't present, so don't mark as partial
  3175. if (hr != MAPI_E_NOT_FOUND)
  3176. bPartial = true;
  3177. continue;
  3178. }
  3179. hr = Util::CopyStream(lpSrcStream, lpDestStream);
  3180. if (hr != hrSuccess)
  3181. bPartial = true;
  3182. }
  3183. // set destination proptags in original properties
  3184. for (ULONG i = 0; i < cValues; ++i) {
  3185. lpProps[i].ulPropTag = lpsDestTagArray->aulPropTag[i];
  3186. // Reset PT_ERROR properties because outlook xp pst doesn't support to set props this.
  3187. if (PROP_TYPE(lpProps[i].ulPropTag) == PT_ERROR)
  3188. lpProps[i].ulPropTag = PR_NULL;
  3189. }
  3190. hr = lpDestProp->SetProps(cValues, lpProps, NULL);
  3191. if (FAILED(hr))
  3192. goto exit;
  3193. // TODO: test how this should work on CopyProps() !!
  3194. if (ulFlags & MAPI_MOVE) {
  3195. // TODO: add problem array
  3196. hr = lpSrcProp->DeleteProps(lpIncludeProps, NULL);
  3197. if (FAILED(hr))
  3198. goto exit;
  3199. }
  3200. exit:
  3201. if (bPartial)
  3202. hr = MAPI_W_PARTIAL_COMPLETION;
  3203. if (hr == hrSuccess && lppProblems != nullptr)
  3204. // may not return a problem set when we have a warning/error code in hr
  3205. *lppProblems = lpProblems.release();
  3206. return hr;
  3207. }
  3208. /**
  3209. * Copy the IMAP data properties if available, with single instance on
  3210. * the IMAP Email.
  3211. *
  3212. * @param[in] lpSrcMsg Copy IMAP data from this message
  3213. * @param[in] lpDstMsg Copy IMAP data to this message
  3214. *
  3215. * @return MAPI error code
  3216. */
  3217. HRESULT Util::HrCopyIMAPData(LPMESSAGE lpSrcMsg, LPMESSAGE lpDstMsg)
  3218. {
  3219. HRESULT hr = hrSuccess;
  3220. object_ptr<IStream> lpSrcStream, lpDestStream;
  3221. static constexpr const SizedSPropTagArray(3, sptaIMAP) =
  3222. {3, {PR_EC_IMAP_EMAIL_SIZE, PR_EC_IMAP_BODY,
  3223. PR_EC_IMAP_BODYSTRUCTURE}};
  3224. ULONG cValues = 0;
  3225. memory_ptr<SPropValue> lpIMAPProps;
  3226. // special case: get PR_EC_IMAP_BODY if present, and copy with single instance
  3227. // hidden property in kopano, try to copy contents
  3228. if (Util::TryOpenProperty(PT_BINARY, PR_EC_IMAP_EMAIL, lpSrcMsg,
  3229. PR_EC_IMAP_EMAIL, lpDstMsg, &~lpSrcStream, &~lpDestStream) != hrSuccess ||
  3230. Util::CopyStream(lpSrcStream, lpDestStream) != hrSuccess)
  3231. return hrSuccess;
  3232. /*
  3233. * Try making a single instance copy for IMAP body data (without sending the data to server).
  3234. * No error checking, we do not care if this fails, we still have all the data.
  3235. */
  3236. Util::CopyInstanceIds(lpSrcMsg, lpDstMsg);
  3237. // Since we have a copy of the original email body, copy the other properties for IMAP too
  3238. hr = lpSrcMsg->GetProps(sptaIMAP, 0, &cValues, &~lpIMAPProps);
  3239. if (FAILED(hr))
  3240. return hr;
  3241. hr = lpDstMsg->SetProps(cValues, lpIMAPProps, NULL);
  3242. if (FAILED(hr))
  3243. return hr;
  3244. return hrSuccess;
  3245. }
  3246. HRESULT Util::HrDeleteIMAPData(LPMESSAGE lpMsg)
  3247. {
  3248. static constexpr const SizedSPropTagArray(4, sptaIMAP) =
  3249. {4, { PR_EC_IMAP_EMAIL_SIZE, PR_EC_IMAP_EMAIL,
  3250. PR_EC_IMAP_BODY, PR_EC_IMAP_BODYSTRUCTURE}};
  3251. return lpMsg->DeleteProps(sptaIMAP, NULL);
  3252. }
  3253. /**
  3254. * Get the quota status object for a store with given quota limits.
  3255. *
  3256. * @param[in] lpMsgStore Store to get the quota for
  3257. * @param[in] lpsQuota The (optional) quota limits to check
  3258. * @param[out] lppsQuotaStatus Quota status struct
  3259. *
  3260. * @return MAPI error code
  3261. */
  3262. HRESULT Util::HrGetQuotaStatus(IMsgStore *lpMsgStore, ECQUOTA *lpsQuota,
  3263. ECQUOTASTATUS **lppsQuotaStatus)
  3264. {
  3265. HRESULT hr = hrSuccess;
  3266. memory_ptr<ECQUOTASTATUS> lpsQuotaStatus;
  3267. memory_ptr<SPropValue> lpProps;
  3268. static constexpr const SizedSPropTagArray(1, sptaProps) = {1, {PR_MESSAGE_SIZE_EXTENDED}};
  3269. ULONG cValues = 0;
  3270. if (lpMsgStore == nullptr || lppsQuotaStatus == nullptr)
  3271. return MAPI_E_INVALID_PARAMETER;
  3272. hr = lpMsgStore->GetProps(sptaProps, 0, &cValues, &~lpProps);
  3273. if (hr != hrSuccess)
  3274. return hr;
  3275. if (cValues != 1 || lpProps[0].ulPropTag != PR_MESSAGE_SIZE_EXTENDED)
  3276. return MAPI_E_NOT_FOUND;
  3277. hr = MAPIAllocateBuffer(sizeof *lpsQuotaStatus, &~lpsQuotaStatus);
  3278. if (hr != hrSuccess)
  3279. return hr;
  3280. memset(lpsQuotaStatus, 0, sizeof *lpsQuotaStatus);
  3281. lpsQuotaStatus->llStoreSize = lpProps[0].Value.li.QuadPart;
  3282. lpsQuotaStatus->quotaStatus = QUOTA_OK;
  3283. if (lpsQuota && lpsQuotaStatus->llStoreSize > 0) {
  3284. if (lpsQuota->llHardSize > 0 && lpsQuotaStatus->llStoreSize > lpsQuota->llHardSize)
  3285. lpsQuotaStatus->quotaStatus = QUOTA_HARDLIMIT;
  3286. else if (lpsQuota->llSoftSize > 0 && lpsQuotaStatus->llStoreSize > lpsQuota->llSoftSize)
  3287. lpsQuotaStatus->quotaStatus = QUOTA_SOFTLIMIT;
  3288. else if (lpsQuota->llWarnSize > 0 && lpsQuotaStatus->llStoreSize > lpsQuota->llWarnSize)
  3289. lpsQuotaStatus->quotaStatus = QUOTA_WARN;
  3290. }
  3291. *lppsQuotaStatus = lpsQuotaStatus.release();
  3292. return hrSuccess;
  3293. }
  3294. /**
  3295. * Removes properties from lpDestMsg, which do are not listed in
  3296. * lpsValidProps.
  3297. *
  3298. * Named properties listed in lpsValidProps map to names in
  3299. * lpSourceMsg. The corresponding property tags are checked in
  3300. * lpDestMsg.
  3301. *
  3302. * @param[out] lpDestMsg The message to delete properties from, which are found "invalid"
  3303. * @param[in] lpSourceMsg The message for which named properties may be lookupped, listed in lpsValidProps
  3304. * @param[in] lpsValidProps Properties which are valid in lpDestMsg. All others should be removed.
  3305. *
  3306. * @return MAPI error code
  3307. */
  3308. HRESULT Util::HrDeleteResidualProps(LPMESSAGE lpDestMsg, LPMESSAGE lpSourceMsg, LPSPropTagArray lpsValidProps)
  3309. {
  3310. HRESULT hr = hrSuccess;
  3311. memory_ptr<SPropTagArray> lpsPropArray, lpsNamedPropArray;
  3312. memory_ptr<SPropTagArray> lpsMappedPropArray;
  3313. ULONG cPropNames = 0;
  3314. memory_ptr<MAPINAMEID *> lppPropNames;
  3315. PropTagSet sPropTagSet;
  3316. if (lpDestMsg == nullptr || lpSourceMsg == nullptr || lpsValidProps == nullptr)
  3317. return MAPI_E_INVALID_PARAMETER;
  3318. hr = lpDestMsg->GetPropList(0, &~lpsPropArray);
  3319. if (hr != hrSuccess || lpsPropArray->cValues == 0)
  3320. return hr;
  3321. hr = MAPIAllocateBuffer(CbNewSPropTagArray(lpsValidProps->cValues), &~lpsNamedPropArray);
  3322. if (hr != hrSuccess)
  3323. return hr;
  3324. memset(lpsNamedPropArray, 0, CbNewSPropTagArray(lpsValidProps->cValues));
  3325. for (unsigned i = 0; i < lpsValidProps->cValues; ++i)
  3326. if (PROP_ID(lpsValidProps->aulPropTag[i]) >= 0x8000)
  3327. lpsNamedPropArray->aulPropTag[lpsNamedPropArray->cValues++] = lpsValidProps->aulPropTag[i];
  3328. if (lpsNamedPropArray->cValues > 0) {
  3329. hr = lpSourceMsg->GetNamesFromIDs(&+lpsNamedPropArray, NULL, 0, &cPropNames, &~lppPropNames);
  3330. if (FAILED(hr))
  3331. return hr;
  3332. hr = lpDestMsg->GetIDsFromNames(cPropNames, lppPropNames, MAPI_CREATE, &~lpsMappedPropArray);
  3333. if (FAILED(hr))
  3334. return hr;
  3335. }
  3336. // Add the PropTags the message currently has
  3337. for (unsigned i = 0; i < lpsPropArray->cValues; ++i)
  3338. sPropTagSet.insert(lpsPropArray->aulPropTag[i]);
  3339. // Remove the regular properties we want to keep
  3340. for (unsigned i = 0; i < lpsValidProps->cValues; ++i)
  3341. if (PROP_ID(lpsValidProps->aulPropTag[i]) < 0x8000)
  3342. sPropTagSet.erase(lpsValidProps->aulPropTag[i]);
  3343. // Remove the mapped named properties we want to keep. Filter failed named properties, so they will be removed
  3344. for (unsigned i = 0; lpsMappedPropArray != NULL && i < lpsMappedPropArray->cValues; ++i)
  3345. if (PROP_TYPE(lpsMappedPropArray->aulPropTag[i]) != PT_ERROR)
  3346. sPropTagSet.erase(lpsMappedPropArray->aulPropTag[i]);
  3347. if (sPropTagSet.empty())
  3348. return hrSuccess;
  3349. // Reuse lpsPropArray to hold the properties we're going to delete
  3350. assert(lpsPropArray->cValues >= sPropTagSet.size());
  3351. memset(lpsPropArray->aulPropTag, 0, lpsPropArray->cValues * sizeof *lpsPropArray->aulPropTag);
  3352. lpsPropArray->cValues = 0;
  3353. for (const auto &i : sPropTagSet)
  3354. lpsPropArray->aulPropTag[lpsPropArray->cValues++] = i;
  3355. hr = lpDestMsg->DeleteProps(lpsPropArray, NULL);
  3356. if (hr != hrSuccess)
  3357. return hr;
  3358. return lpDestMsg->SaveChanges(KEEP_OPEN_READWRITE);
  3359. }
  3360. /**
  3361. * Find an EntryID using exact binary matches in an array of
  3362. * properties.
  3363. *
  3364. * @param[in] cbEID number of bytes in lpEID
  3365. * @param[in] lpEID the EntryID to find in the property array
  3366. * @param[in] cbEntryIDs number of properties in lpEntryIDs
  3367. * @param[in] lpEntryIDs array of entryid properties
  3368. * @param[out] lpbFound TRUE if folder was found
  3369. * @param[out] lpPos index number the folder was found in if *lpbFound is TRUE, otherwise untouched
  3370. *
  3371. * @return MAPI error code
  3372. * @retval MAPI_E_INVALID_PARAMETER a passed parameter was invalid
  3373. */
  3374. HRESULT Util::HrFindEntryIDs(ULONG cbEID, LPENTRYID lpEID, ULONG cbEntryIDs, LPSPropValue lpEntryIDs, BOOL *lpbFound, ULONG* lpPos)
  3375. {
  3376. BOOL bFound = FALSE;
  3377. ULONG i;
  3378. if (cbEID == 0 || lpEID == NULL || cbEntryIDs == 0 ||
  3379. lpEntryIDs == NULL || lpbFound == NULL)
  3380. return MAPI_E_INVALID_PARAMETER;
  3381. for (i = 0; bFound == FALSE && i < cbEntryIDs; ++i) {
  3382. if (PROP_TYPE(lpEntryIDs[i].ulPropTag) != PT_BINARY)
  3383. continue;
  3384. if (cbEID != lpEntryIDs[i].Value.bin.cb)
  3385. continue;
  3386. if (memcmp(lpEID, lpEntryIDs[i].Value.bin.lpb, cbEID) == 0) {
  3387. bFound = TRUE;
  3388. break;
  3389. }
  3390. }
  3391. *lpbFound = bFound;
  3392. if (bFound && lpPos)
  3393. *lpPos = i;
  3394. return hrSuccess;
  3395. }
  3396. HRESULT Util::HrDeleteAttachments(LPMESSAGE lpMsg)
  3397. {
  3398. MAPITablePtr ptrAttachTable;
  3399. SRowSetPtr ptrRows;
  3400. static constexpr const SizedSPropTagArray(1, sptaAttachNum) = {1, {PR_ATTACH_NUM}};
  3401. if (lpMsg == NULL)
  3402. return MAPI_E_INVALID_PARAMETER;
  3403. HRESULT hr = lpMsg->GetAttachmentTable(0, &~ptrAttachTable);
  3404. if (hr != hrSuccess)
  3405. return hr;
  3406. hr = HrQueryAllRows(ptrAttachTable, sptaAttachNum, NULL, NULL, 0, &ptrRows);
  3407. if (hr != hrSuccess)
  3408. return hr;
  3409. for (SRowSetPtr::size_type i = 0; i < ptrRows.size(); ++i) {
  3410. hr = lpMsg->DeleteAttach(ptrRows[i].lpProps[0].Value.l, 0, NULL, 0);
  3411. if (hr != hrSuccess)
  3412. return hr;
  3413. }
  3414. return hrSuccess;
  3415. }
  3416. HRESULT Util::HrDeleteRecipients(LPMESSAGE lpMsg)
  3417. {
  3418. MAPITablePtr ptrRecipTable;
  3419. SRowSetPtr ptrRows;
  3420. static constexpr const SizedSPropTagArray(1, sptaRowId) = {1, {PR_ROWID}};
  3421. if (lpMsg == NULL)
  3422. return MAPI_E_INVALID_PARAMETER;
  3423. HRESULT hr = lpMsg->GetRecipientTable(0, &~ptrRecipTable);
  3424. if (hr != hrSuccess)
  3425. return hr;
  3426. hr = HrQueryAllRows(ptrRecipTable, sptaRowId, NULL, NULL, 0, &ptrRows);
  3427. if (hr != hrSuccess)
  3428. return hr;
  3429. return lpMsg->ModifyRecipients(MODRECIP_REMOVE, (LPADRLIST)ptrRows.get());
  3430. }
  3431. HRESULT Util::HrDeleteMessage(IMAPISession *lpSession, IMessage *lpMessage)
  3432. {
  3433. ULONG cMsgProps;
  3434. SPropArrayPtr ptrMsgProps;
  3435. MsgStorePtr ptrStore;
  3436. ULONG ulType;
  3437. MAPIFolderPtr ptrFolder;
  3438. ENTRYLIST entryList = {1, NULL};
  3439. static constexpr const SizedSPropTagArray(3, sptaMessageProps) =
  3440. {3, {PR_ENTRYID, PR_STORE_ENTRYID, PR_PARENT_ENTRYID}};
  3441. enum {IDX_ENTRYID, IDX_STORE_ENTRYID, IDX_PARENT_ENTRYID};
  3442. HRESULT hr = lpMessage->GetProps(sptaMessageProps, 0, &cMsgProps, &~ptrMsgProps);
  3443. if (hr != hrSuccess)
  3444. return hr;
  3445. hr = lpSession->OpenMsgStore(0, ptrMsgProps[IDX_STORE_ENTRYID].Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrMsgProps[IDX_STORE_ENTRYID].Value.bin.lpb), &ptrStore.iid(), MDB_WRITE, &~ptrStore);
  3446. if (hr != hrSuccess)
  3447. return hr;
  3448. hr = ptrStore->OpenEntry(ptrMsgProps[IDX_PARENT_ENTRYID].Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrMsgProps[IDX_PARENT_ENTRYID].Value.bin.lpb), &ptrFolder.iid(), MAPI_MODIFY, &ulType, &~ptrFolder);
  3449. if (hr != hrSuccess)
  3450. return hr;
  3451. entryList.cValues = 1;
  3452. entryList.lpbin = &ptrMsgProps[IDX_ENTRYID].Value.bin;
  3453. return ptrFolder->DeleteMessages(&entryList, 0, NULL, DELETE_HARD_DELETE);
  3454. }
  3455. /**
  3456. * Read a property via OpenProperty and put the output in std::string
  3457. *
  3458. * @param[in] lpProp Object to read from
  3459. * @param[in] ulPropTag Proptag to open
  3460. * @param[out] strData String to write to
  3461. * @return result
  3462. */
  3463. HRESULT Util::ReadProperty(IMAPIProp *lpProp, ULONG ulPropTag, std::string &strData)
  3464. {
  3465. HRESULT hr = hrSuccess;
  3466. object_ptr<IStream> lpStream;
  3467. hr = lpProp->OpenProperty(ulPropTag, &IID_IStream, 0, 0, &~lpStream);
  3468. if(hr != hrSuccess)
  3469. return hr;
  3470. return HrStreamToString(lpStream, strData);
  3471. }
  3472. /**
  3473. * Write a property using OpenProperty()
  3474. *
  3475. * This function will open a stream to the given property and write all data from strData into
  3476. * it usin STGM_DIRECT and MAPI_MODIFY | MAPI_CREATE. This means the existing data will be over-
  3477. * written
  3478. *
  3479. * @param[in] lpProp Object to write to
  3480. * @param[in] ulPropTag Property to write
  3481. * @param[in] strData Data to write
  3482. * @return result
  3483. */
  3484. HRESULT Util::WriteProperty(IMAPIProp *lpProp, ULONG ulPropTag, const std::string &strData)
  3485. {
  3486. HRESULT hr = hrSuccess;
  3487. object_ptr<IStream> lpStream;
  3488. ULONG len = 0;
  3489. hr = lpProp->OpenProperty(ulPropTag, &IID_IStream, STGM_DIRECT, MAPI_CREATE | MAPI_MODIFY, &~lpStream);
  3490. if(hr != hrSuccess)
  3491. return hr;
  3492. hr = lpStream->Write(strData.data(), strData.size(), &len);
  3493. if(hr != hrSuccess)
  3494. return hr;
  3495. return lpStream->Commit(0);
  3496. }
  3497. HRESULT Util::ExtractRSSEntryID(LPSPropValue lpPropBlob, ULONG *lpcbEntryID, LPENTRYID *lppEntryID)
  3498. {
  3499. return ExtractAdditionalRenEntryID(lpPropBlob, RSF_PID_RSS_SUBSCRIPTION, lpcbEntryID, lppEntryID);
  3500. }
  3501. HRESULT Util::ExtractSuggestedContactsEntryID(LPSPropValue lpPropBlob, ULONG *lpcbEntryID, LPENTRYID *lppEntryID)
  3502. {
  3503. return ExtractAdditionalRenEntryID(lpPropBlob, RSF_PID_SUGGESTED_CONTACTS , lpcbEntryID, lppEntryID);
  3504. }
  3505. HRESULT Util::ExtractAdditionalRenEntryID(LPSPropValue lpPropBlob, unsigned short usBlockType, ULONG *lpcbEntryID, LPENTRYID *lppEntryID)
  3506. {
  3507. HRESULT hr;
  3508. LPBYTE lpPos = lpPropBlob->Value.bin.lpb;
  3509. LPBYTE lpEnd = lpPropBlob->Value.bin.lpb + lpPropBlob->Value.bin.cb;
  3510. while (true) {
  3511. if (lpPos + 8 > lpEnd)
  3512. return MAPI_E_NOT_FOUND;
  3513. if (*reinterpret_cast<unsigned short *>(lpPos) == 0)
  3514. return MAPI_E_NOT_FOUND;
  3515. if (*(unsigned short *)lpPos != usBlockType) {
  3516. unsigned short usLen = 0;
  3517. lpPos += 2; // Skip ID
  3518. usLen = *(unsigned short*)lpPos;
  3519. lpPos += 2;
  3520. if (lpPos + usLen > lpEnd)
  3521. return MAPI_E_CORRUPT_DATA;
  3522. lpPos += usLen;
  3523. continue;
  3524. }
  3525. unsigned short usLen = 0;
  3526. lpPos += 4; // Skip ID + total length
  3527. if (*reinterpret_cast<unsigned short *>(lpPos) != RSF_ELID_ENTRYID)
  3528. return MAPI_E_CORRUPT_DATA;
  3529. lpPos += 2; // Skip check
  3530. usLen = *(unsigned short *)lpPos;
  3531. lpPos += 2;
  3532. if (lpPos + usLen > lpEnd)
  3533. return MAPI_E_CORRUPT_DATA;
  3534. hr = MAPIAllocateBuffer(usLen, (LPVOID*)lppEntryID);
  3535. if (hr != hrSuccess)
  3536. return hr;
  3537. memcpy(*lppEntryID, lpPos, usLen);
  3538. *lpcbEntryID = usLen;
  3539. return hrSuccess;
  3540. }
  3541. return hrSuccess;
  3542. }
  3543. } /* namespace */