vconverter.cpp 127 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398
  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/platform.h>
  18. #include <memory>
  19. #include <utility>
  20. #include <kopano/ECRestriction.h>
  21. #include "vconverter.h"
  22. #include "valarm.h"
  23. #include "icalrecurrence.h"
  24. #include <mapi.h>
  25. #include <mapiutil.h>
  26. #include <kopano/mapiext.h>
  27. #include <kopano/memory.hpp>
  28. #include <kopano/tie.hpp>
  29. #include <kopano/CommonUtil.h>
  30. #include <kopano/Util.h>
  31. #include "icaluid.h"
  32. #include "nameids.h"
  33. #include <kopano/stringutil.h>
  34. #include <ctime>
  35. #include <kopano/mapi_ptr.h>
  36. #include <kopano/namedprops.h>
  37. #include "icalmem.hpp"
  38. using namespace std;
  39. using namespace KCHL;
  40. namespace KC {
  41. /**
  42. * Copies string from source to destination
  43. *
  44. * The function also does charset conversion according to the ECIConv object passed
  45. *
  46. * @param[in] lpIConv ECIConv object pointer for charset conversion, may be NULL for no conversion
  47. * @param[in] base Base from memory allocation, cannot not be NULL
  48. * @param[in] lpszSrc Source chararacter string, NULL allowed, since icalproperty_get_*() may return NULL
  49. * @param[out] lppszDst Destination char pointer, cannot be NULL
  50. * @return MAPI error code
  51. */
  52. // expect input to be UTF-8 from libical ?
  53. HRESULT HrCopyString(convert_context& converter, std::string& strCharset, void *base, const char* lpszSrc, WCHAR** lppszDst)
  54. {
  55. std::wstring strWide;
  56. if (lpszSrc)
  57. strWide = converter.convert_to<wstring>(lpszSrc, rawsize(lpszSrc), strCharset.c_str());
  58. return HrCopyString(base, strWide.c_str(), lppszDst);
  59. }
  60. HRESULT HrCopyString(void *base, const WCHAR* lpwszSrc, WCHAR** lppwszDst)
  61. {
  62. WCHAR* lpwszDst = NULL;
  63. std::wstring strText;
  64. if(!lpwszSrc)
  65. strText.clear();
  66. else
  67. strText = lpwszSrc;
  68. HRESULT hr = MAPIAllocateMore((strText.length() + 1) * sizeof(WCHAR),
  69. base, reinterpret_cast<void **>(&lpwszDst));
  70. if (hr != hrSuccess)
  71. return hr;
  72. wcsncpy(lpwszDst, strText.c_str(), strText.length()+1);
  73. *lppwszDst = lpwszDst;
  74. return hrSuccess;
  75. }
  76. /**
  77. * @param[in] lpAdrBook Mapi addresss book
  78. * @param[in] mapTimeZones std::map containing timezone names and corresponding timezone structures
  79. * @param[in] lpNamedProps Named property tag array
  80. * @param[in] strCharset Charset to convert the final item to
  81. * @param[in] blCensor Censor some properties for private items if set to true
  82. * @param[in] bNoRecipients Skip recipients during conversion if set to true
  83. * @param[in] lpMailUser IMailUser Object pointer of the logged in user
  84. */
  85. VConverter::VConverter(LPADRBOOK lpAdrBook, timezone_map *mapTimeZones, LPSPropTagArray lpNamedProps, const std::string& strCharset, bool blCensor, bool bNoRecipients, IMailUser *lpMailUser)
  86. {
  87. m_lpAdrBook = lpAdrBook;
  88. m_mapTimeZones = mapTimeZones;
  89. m_iCurrentTimeZone = m_mapTimeZones->end();
  90. m_lpNamedProps = lpNamedProps;
  91. m_strCharset = strCharset;
  92. m_lpMailUser = lpMailUser;
  93. m_bCensorPrivate = blCensor;
  94. m_bNoRecipients = bNoRecipients;
  95. m_ulUserStatus = 0;
  96. }
  97. /**
  98. * Basic ical to mapi conversion, common to all VEVENT, VTODO,
  99. * VFREEBUSY. It returns an internal icalitem struct, which can later
  100. * be converted into an existing IMessage.
  101. *
  102. * @param[in] lpEventRoot Top level VCALENDAR component class
  103. * @param[in] lpEvent VTODO/VEVENT/VFREEBUSY component which is parsed
  104. * @param[in] lpPrevItem Previous ical component that was parsed, NULL if this is the first item, used for adding exceptions.
  105. * @param[in] lppRet Return structure in which all mapi properties are stored
  106. * @return MAPI error code
  107. */
  108. HRESULT VConverter::HrICal2MAPI(icalcomponent *lpEventRoot, icalcomponent *lpEvent, icalitem *lpPrevItem, icalitem **lppRet)
  109. {
  110. HRESULT hr = hrSuccess;
  111. std::unique_ptr<icalitem> lpIcalItem;
  112. icalproperty_method icMethod;
  113. icalproperty *lpicLastModified = NULL;
  114. icaltimetype icLastModifed;
  115. bool bIsAllday;
  116. // Retrieve the Allday status of the event
  117. hr = HrRetrieveAlldayStatus(lpEvent, &bIsAllday);
  118. if (hr != hrSuccess)
  119. goto exit;
  120. // we might be updating for exceptions
  121. if (*lppRet != NULL && lpPrevItem != NULL && lpPrevItem == *lppRet) {
  122. hr = HrAddException(lpEventRoot, lpEvent, bIsAllday, lpPrevItem);
  123. if (hr == hrSuccess)
  124. goto exit;
  125. }
  126. lpIcalItem.reset(new icalitem);
  127. if ((hr = MAPIAllocateBuffer(sizeof(void*), &lpIcalItem->base)) != hrSuccess)
  128. goto exit;
  129. lpIcalItem->lpRecurrence = NULL;
  130. // ---------------------------
  131. icMethod = icalcomponent_get_method(lpEventRoot);
  132. lpicLastModified = icalcomponent_get_first_property(lpEvent, ICAL_LASTMODIFIED_PROPERTY);
  133. if (lpicLastModified)
  134. icLastModifed = icalproperty_get_lastmodified(lpicLastModified);
  135. else
  136. icLastModifed = icaltime_null_time();
  137. // according to the RFC, LASTMODIFIED is always in UTC
  138. lpIcalItem->tLastModified = icaltime_as_timet(icLastModifed);
  139. // also sets strUid in icalitem struct
  140. hr = HrAddUids(lpEvent, lpIcalItem.get());
  141. if (hr != hrSuccess)
  142. goto exit;
  143. // Handles RECURRENCE-ID tag for exception update
  144. hr = HrAddRecurrenceID(lpEvent, lpIcalItem.get());
  145. if (hr != hrSuccess)
  146. goto exit;
  147. hr = HrAddStaticProps(icMethod, lpIcalItem.get());
  148. if (hr != hrSuccess)
  149. goto exit;
  150. hr = HrAddSimpleHeaders(lpEvent, lpIcalItem.get()); // subject, location, ...
  151. if (hr != hrSuccess)
  152. goto exit;
  153. hr = HrAddXHeaders(lpEvent, lpIcalItem.get());
  154. if (hr != hrSuccess)
  155. goto exit;
  156. hr = HrAddCategories(lpEvent, lpIcalItem.get());
  157. if (hr != hrSuccess)
  158. goto exit;
  159. if (icMethod == ICAL_METHOD_REPLY)
  160. hr = HrAddReplyRecipients(lpEvent, lpIcalItem.get());
  161. else // CANCEL, REQUEST, PUBLISH
  162. hr = HrAddRecipients(lpEvent, lpIcalItem.get(), &lpIcalItem->lstMsgProps, &lpIcalItem->lstRecips);
  163. if (hr != hrSuccess)
  164. goto exit;
  165. hr = HrResolveUser(lpIcalItem->base, &(lpIcalItem->lstRecips));
  166. if (hr != hrSuccess)
  167. goto exit;
  168. // This function uses m_ulUserStatus set by HrResolveUser.
  169. hr = HrAddBaseProperties(icMethod, lpEvent, lpIcalItem->base, false, &lpIcalItem->lstMsgProps);
  170. if (hr != hrSuccess)
  171. goto exit;
  172. hr = HrAddBusyStatus(lpEvent, icMethod, lpIcalItem.get());
  173. if (hr != hrSuccess)
  174. goto exit;
  175. // Important: m_iCurrentTimeZone will be set by this function, because of the possible recurrence lateron
  176. hr = HrAddTimes(icMethod, lpEventRoot, lpEvent, bIsAllday, lpIcalItem.get());
  177. if (hr != hrSuccess)
  178. goto exit;
  179. // Set reminder / alarm
  180. hr = HrAddReminder(lpEventRoot, lpEvent, lpIcalItem.get());
  181. if (hr != hrSuccess)
  182. goto exit;
  183. // Set recurrence.
  184. hr = HrAddRecurrence(lpEventRoot, lpEvent, bIsAllday, lpIcalItem.get());
  185. if (hr != hrSuccess)
  186. goto exit;
  187. *lppRet = lpIcalItem.release();
  188. exit:
  189. if (lpIcalItem != nullptr)
  190. MAPIFreeBuffer(lpIcalItem->base);
  191. return hr;
  192. }
  193. /**
  194. * Returns the UID string from the ical data
  195. *
  196. * @param[in] lpEvent ical component that has to be parsed for uid property
  197. * @param[out] strUid Return string for ical UID property
  198. * @return MAPI error code
  199. * @retval MAPI_E_NOT_FOUND UID property not present in ical data
  200. */
  201. HRESULT VConverter::HrGetUID(icalcomponent *lpEvent, std::string *strUid)
  202. {
  203. const char *uid = NULL;
  204. icalproperty *icProp = icalcomponent_get_first_property(lpEvent,
  205. ICAL_UID_PROPERTY);
  206. if (icProp == NULL)
  207. return MAPI_E_NOT_FOUND;
  208. uid = icalproperty_get_uid(icProp);
  209. if (uid == NULL || strcmp(uid,"") == 0)
  210. return MAPI_E_NOT_FOUND;
  211. *strUid = uid;
  212. return hrSuccess;
  213. }
  214. /**
  215. * Converts string UID to binary property. The converted UID is in a
  216. * format outlook wants, as described here.
  217. *
  218. * UID contents according to [MS-OXCICAL].pdf
  219. * UID = EncodedGlobalId or ThirdPartyGlobalId
  220. *
  221. * EncodedGlobalId = Header GlobalIdData
  222. * ThirdPartyGlobalId = 1*UTF8-octets ; Assuming UTF-8 is the encoding
  223. *
  224. * Header = ByteArrayID InstanceDate CreationDateTime Padding DataSize
  225. *
  226. * ByteArrayID = "040000008200E00074C5B7101A82E008"
  227. * InstanceDate = InstanceYear InstanceMonth InstanceDay
  228. * InstanceYear = 4*4HEXDIGIT ; UInt16
  229. * InstanceMonth = 2*2HEXDIGIT ; UInt8
  230. * InstanceDay = 2*2HEXDIGIT ; UInt8
  231. * CreationDateTime = FileTime
  232. * FileTime = 16*16HEXDIGIT ; UInt6
  233. * Padding = 16*16HEXDIGIT ; "0000000000000000" recommended
  234. * DataSize = 8*8HEXDIGIT ; UInt32 little-endian
  235. * GlobalIdData = 2*HEXDIGIT4
  236. *
  237. * @param[in] strUid String UID
  238. * @param[in] base Base for allocating memory
  239. * @param[out] lpPropValue The binary uid is returned in SPropValue structure
  240. * @return Always return hrSuccess
  241. */
  242. HRESULT VConverter::HrMakeBinaryUID(const std::string &strUid, void *base, SPropValue *lpPropValue)
  243. {
  244. SPropValue sPropValue;
  245. std::string strBinUid;
  246. std::string strByteArrayID = "040000008200E00074C5B7101A82E008";
  247. // Check whether this is a default Outlook UID
  248. // Exchange example: UID:040000008200E00074C5B7101A82E008 00000000 305D0F2A9A06C901 0000000000000000 10000000 7F64D28AE2DCC64C88F849733F5FBD1D
  249. // GMail example: UID:rblkvqecgurvb0all6rjb3d1j8@google.com
  250. // Sunbird example: UID:1090c3de-36b2-4352-a155-a1436bc806b8
  251. if (strUid.compare(0, strByteArrayID.length(), strByteArrayID) == 0)
  252. // EncodedGlobalId
  253. strBinUid = hex2bin(strUid);
  254. else
  255. // ThirdPartyGlobalId
  256. HrMakeBinUidFromICalUid(strUid, &strBinUid);
  257. // Caller sets .ulPropTag
  258. sPropValue.Value.bin.cb = strBinUid.size();
  259. HRESULT hr = MAPIAllocateMore(sPropValue.Value.bin.cb, base,
  260. reinterpret_cast<void **>(&sPropValue.Value.bin.lpb));
  261. if (hr != hrSuccess)
  262. return hr;
  263. memcpy(sPropValue.Value.bin.lpb, strBinUid.data(), sPropValue.Value.bin.cb);
  264. // set return value
  265. lpPropValue->Value.bin.cb = sPropValue.Value.bin.cb;
  266. lpPropValue->Value.bin.lpb = sPropValue.Value.bin.lpb;
  267. return hrSuccess;
  268. }
  269. /**
  270. * Check if the user passed in param is the logged in user
  271. *
  272. * @param[in] strUser User name of user
  273. * @return True if the user is logged in else false
  274. */
  275. bool VConverter::bIsUserLoggedIn(const std::wstring &strUser)
  276. {
  277. HRESULT hr = hrSuccess;
  278. memory_ptr<SPropValue> lpUserProp;
  279. if (m_lpMailUser)
  280. hr = HrGetOneProp(m_lpMailUser, PR_SMTP_ADDRESS_W, &~lpUserProp);
  281. else
  282. hr = MAPI_E_CALL_FAILED;
  283. if (hr != hrSuccess)
  284. return false;
  285. return wcsncmp(lpUserProp->Value.lpszW, strUser.c_str(), strUser.length()) == 0;
  286. }
  287. /**
  288. * Resolves the recipient email address of the users to correct user's
  289. * name and entry-id from addressbook.
  290. *
  291. * @param[in] base Base for memory allocation, cannot be NULL
  292. * @param[in,out] lplstIcalRecip List of recipients which are to be resolved, resolved users are returned in list
  293. * @return MAPI error code
  294. */
  295. HRESULT VConverter::HrResolveUser(void *base , std::list<icalrecip> *lplstIcalRecip)
  296. {
  297. if (m_lpAdrBook == nullptr)
  298. /* no resolution attempted, as done from testsuite. */
  299. return hrSuccess;
  300. HRESULT hr = hrSuccess;
  301. memory_ptr<SPropValue> lpUsrEidProp;
  302. adrlist_ptr lpAdrList;
  303. memory_ptr<ENTRYID> lpDDEntryID;
  304. ULONG cbDDEntryID;
  305. object_ptr<IABContainer> lpAddrFolder;
  306. memory_ptr<FlagList> lpFlagList;
  307. icalrecip icalRecipient;
  308. ULONG ulRecpCnt = 0;
  309. ULONG ulRetn = 0;
  310. ULONG ulObjType = 0;
  311. ULONG cbEID = 0;
  312. if (lplstIcalRecip->empty())
  313. return hr;
  314. // ignore error
  315. if(m_lpMailUser)
  316. HrGetOneProp(m_lpMailUser, PR_ENTRYID, &~lpUsrEidProp);
  317. ulRecpCnt = lplstIcalRecip->size();
  318. hr = MAPIAllocateBuffer(CbNewFlagList(ulRecpCnt), &~lpFlagList);
  319. if (hr != hrSuccess)
  320. return hr;
  321. lpFlagList->cFlags = ulRecpCnt;
  322. hr = MAPIAllocateBuffer(CbNewADRLIST(ulRecpCnt), &~lpAdrList);
  323. if (hr != hrSuccess)
  324. return hr;
  325. lpAdrList->cEntries = ulRecpCnt;
  326. ulRecpCnt = 0;
  327. for (const auto &recip : *lplstIcalRecip) {
  328. lpAdrList->aEntries[ulRecpCnt].cValues = 1;
  329. hr = MAPIAllocateBuffer(sizeof(SPropValue), (void **) &lpAdrList->aEntries[ulRecpCnt].rgPropVals);
  330. if (hr != hrSuccess)
  331. return hr;
  332. lpAdrList->aEntries[ulRecpCnt].rgPropVals[0].ulPropTag = PR_DISPLAY_NAME_W;
  333. lpAdrList->aEntries[ulRecpCnt].rgPropVals[0].Value.lpszW = const_cast<wchar_t *>(recip.strEmail.c_str());
  334. lpFlagList->ulFlag[ulRecpCnt++] = MAPI_UNRESOLVED;
  335. }
  336. hr = m_lpAdrBook->GetDefaultDir(&cbDDEntryID, &~lpDDEntryID);
  337. if (hr != hrSuccess)
  338. return hr;
  339. hr = m_lpAdrBook->OpenEntry(cbDDEntryID, lpDDEntryID, &IID_IABContainer, 0, &ulObjType, &~lpAddrFolder);
  340. if (hr != hrSuccess)
  341. return hr;
  342. hr = lpAddrFolder->ResolveNames(NULL, MAPI_UNICODE, lpAdrList, lpFlagList);
  343. if (hr != hrSuccess)
  344. return hr;
  345. //reset the recepients with mapped names
  346. for (icalRecipient = lplstIcalRecip->front(), ulRecpCnt = 0;
  347. ulRecpCnt < lplstIcalRecip->size(); ++ulRecpCnt) {
  348. if (lpFlagList->ulFlag[ulRecpCnt] == MAPI_RESOLVED)
  349. {
  350. auto lpMappedProp = PCpropFindProp(lpAdrList->aEntries[ulRecpCnt].rgPropVals, lpAdrList->aEntries[ulRecpCnt].cValues, PR_DISPLAY_NAME_W);
  351. if (lpMappedProp)
  352. icalRecipient.strName = lpMappedProp->Value.lpszW;
  353. }
  354. //save the logged in user's satus , used in setting FB status
  355. auto lpMappedProp = PCpropFindProp(lpAdrList->aEntries[ulRecpCnt].rgPropVals, lpAdrList->aEntries[ulRecpCnt].cValues, PR_ENTRYID);
  356. if (lpMappedProp && lpUsrEidProp)
  357. hr = m_lpAdrBook->CompareEntryIDs(lpUsrEidProp->Value.bin.cb, (LPENTRYID)lpUsrEidProp->Value.bin.lpb, lpMappedProp->Value.bin.cb, (LPENTRYID)lpMappedProp->Value.bin.lpb , 0 , &ulRetn);
  358. if (hr == hrSuccess && ulRetn == TRUE)
  359. m_ulUserStatus = icalRecipient.ulTrackStatus;
  360. //Create EntryID by using mapped names, ical data might not have names.
  361. if (lpFlagList->ulFlag[ulRecpCnt] == MAPI_RESOLVED && lpMappedProp) {
  362. hr = MAPIAllocateMore(lpMappedProp->Value.bin.cb, base, (void**)&icalRecipient.lpEntryID);
  363. if (hr != hrSuccess)
  364. return hr;
  365. icalRecipient.cbEntryID = lpMappedProp->Value.bin.cb;
  366. memcpy(icalRecipient.lpEntryID, lpMappedProp->Value.bin.lpb, lpMappedProp->Value.bin.cb);
  367. } else {
  368. memory_ptr<ENTRYID> lpEID;
  369. hr = ECCreateOneOff((LPTSTR)icalRecipient.strName.c_str(), (LPTSTR)L"SMTP", (LPTSTR)icalRecipient.strEmail.c_str(), MAPI_UNICODE, &cbEID, &~lpEID);
  370. if (hr == hrSuccess) {
  371. // realloc on lpIcalItem
  372. hr = MAPIAllocateMore(cbEID, base, (void**)&icalRecipient.lpEntryID);
  373. if (hr != hrSuccess)
  374. return hr;
  375. icalRecipient.cbEntryID = cbEID;
  376. memcpy(icalRecipient.lpEntryID, lpEID, cbEID);
  377. }
  378. }
  379. lplstIcalRecip->push_back(icalRecipient);
  380. lplstIcalRecip->pop_front();
  381. icalRecipient = lplstIcalRecip->front();
  382. }
  383. return hrSuccess;
  384. }
  385. /**
  386. * Compare UIDs in icalitem and ical component.
  387. *
  388. * @param[in] lpIcalItem icalitem structure containing mapi properties
  389. * @param[in] lpicEvent ical component containing UID property
  390. * @return MAPI error code
  391. * @retval MAPI_E_BAD_VALUE UIDs don't match
  392. */
  393. HRESULT VConverter::HrCompareUids(icalitem *lpIcalItem, icalcomponent *lpicEvent)
  394. {
  395. HRESULT hr = hrSuccess;
  396. memory_ptr<SPropValue> lpPropVal;
  397. std::string strUid;
  398. int res;
  399. hr = HrGetUID(lpicEvent, &strUid);
  400. if (hr != hrSuccess)
  401. return hr;
  402. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpPropVal);
  403. if (hr != hrSuccess)
  404. return hr;
  405. hr = HrMakeBinaryUID(strUid, lpPropVal, lpPropVal);
  406. if (hr != hrSuccess)
  407. return hr;
  408. lpPropVal->ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_GOID], PT_BINARY);
  409. hr = Util::CompareProp(lpPropVal, &lpIcalItem->sBinGuid, createLocaleFromName(""), &res);
  410. if (hr != hrSuccess || res != 0)
  411. hr = MAPI_E_BAD_VALUE;
  412. return hr;
  413. }
  414. /**
  415. * Sets UID property in icalitem mapi structure from the ical component.
  416. *
  417. * @param[in] lpicEvent ical component containing UID ical property
  418. * @param[in] lpIcalItem icalitem structure in which UID is stored
  419. * @return MAPI error code
  420. */
  421. HRESULT VConverter::HrAddUids(icalcomponent *lpicEvent, icalitem *lpIcalItem)
  422. {
  423. SPropValue sPropValue;
  424. std::string strUid;
  425. // GlobalObjectId -> it has UID value & embeded Exception occurnece date for exceptions else 00000000
  426. // CleanGlobalObjectID -> it has UID value
  427. // Get Unique ID of ical item, or create new
  428. HRESULT hr = HrGetUID(lpicEvent, &strUid);
  429. if (hr != hrSuccess)
  430. hr = HrGenerateUid(&strUid);
  431. if (hr != hrSuccess)
  432. return hr;
  433. hr = HrMakeBinaryUID(strUid, lpIcalItem->base, &sPropValue);
  434. if (hr != hrSuccess)
  435. return hr;
  436. // sets exception date in GUID from recurrence-id
  437. hr = HrHandleExceptionGuid(lpicEvent, lpIcalItem->base, &sPropValue);
  438. if (hr != hrSuccess)
  439. return hr;
  440. // set as dispidGlobalObjectID ...
  441. sPropValue.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_GOID], PT_BINARY);
  442. lpIcalItem->lstMsgProps.push_back(sPropValue);
  443. // replace date in GUID, the dispidCleanGlobalObjectID should be same for exceptions and reccurence message.
  444. // used for exceptions in outlook
  445. if(IsOutlookUid(strUid))
  446. strUid.replace(32, 8, "00000000");
  447. hr = HrMakeBinaryUID(strUid, lpIcalItem->base, &sPropValue);
  448. if (hr != hrSuccess)
  449. return hr;
  450. // set as dispidCleanGlobalObjectID...
  451. sPropValue.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_CLEANID], PT_BINARY);
  452. lpIcalItem->lstMsgProps.push_back(sPropValue);
  453. // save the strUid to lookup for occurrences
  454. lpIcalItem->sBinGuid = sPropValue;
  455. return hrSuccess;
  456. }
  457. /**
  458. * Sets the recurrence-id date in GlobalObjectID for exceptions in
  459. * SPropValue structure.
  460. *
  461. * @param[in] lpiEvent ical component containing the recurrence-id property
  462. * @param[in] base Base for memory allocation
  463. * @param[in,out] lpsProp SPropValue is modified to set the new GlobalObjectId
  464. *
  465. * @return MAPI error code
  466. * @retval MAPI_E_INVALID_PARAMETER NULL for lpsProp parameter
  467. */
  468. HRESULT VConverter::HrHandleExceptionGuid(icalcomponent *lpiEvent, void *base, SPropValue *lpsProp)
  469. {
  470. std::string strUid;
  471. std::string strBinUid;
  472. icalproperty *icProp = NULL;
  473. icaltimetype icTime;
  474. char strHexDate[] = "00000000";
  475. if (lpsProp == NULL)
  476. return MAPI_E_INVALID_PARAMETER;
  477. icProp = icalcomponent_get_first_property(lpiEvent, ICAL_RECURRENCEID_PROPERTY);
  478. if (icProp == NULL)
  479. return hrSuccess; //ignoring Recurrence-ID.
  480. strUid = bin2hex(lpsProp->Value.bin.cb, lpsProp->Value.bin.lpb);
  481. icTime = icaltime_from_timet_with_zone(ICalTimeTypeToUTC(lpiEvent, icProp), 0, nullptr);
  482. sprintf(strHexDate,"%04x%02x%02x", icTime.year, icTime.month, icTime.day);
  483. // Exception date is stored in GlobalObjectId
  484. strUid.replace(32, 8, strHexDate);
  485. strBinUid = hex2bin(strUid);
  486. lpsProp->Value.bin.cb = strBinUid.size();
  487. HRESULT hr = MAPIAllocateMore(strBinUid.size(), base,
  488. reinterpret_cast<void **>(&lpsProp->Value.bin.lpb));
  489. if (hr != hrSuccess)
  490. return hr;
  491. memcpy(lpsProp->Value.bin.lpb, strBinUid.data(), lpsProp->Value.bin.cb);
  492. return hrSuccess;
  493. }
  494. /**
  495. * Sets Recurrence-id property for exceptions in mapi structure
  496. *
  497. * @param[in] lpiEvent ical component containing the recurrence-id property
  498. * @param[in,out] lpIcalItem icalitem structure in which the mapi properties are stored
  499. * @return Always returns hrSuccess
  500. */
  501. HRESULT VConverter::HrAddRecurrenceID(icalcomponent *lpiEvent, icalitem *lpIcalItem)
  502. {
  503. SPropValue sPropVal;
  504. icalproperty *icProp = icalcomponent_get_first_property(lpiEvent,
  505. ICAL_RECURRENCEID_PROPERTY);
  506. if (icProp == NULL)
  507. return hrSuccess;
  508. // if RECURRENCE-ID is date then series is all day,
  509. // so set the following properties as a flag to know if series is all day or not.
  510. if (icalproperty_get_recurrenceid(icProp).is_date)
  511. {
  512. // set RecurStartTime as 00:00 AM
  513. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURSTARTTIME], PT_LONG);
  514. sPropVal.Value.ul = 0;
  515. lpIcalItem->lstMsgProps.push_back(sPropVal);
  516. // set RecurEndTime as 12:00 PM (24 hours)
  517. // 60 sec -> highest pow of 2 after 60 -> 64
  518. // 60 mins -> 60 * 64 = 3840 -> highest pow of 2 after 3840 -> 4096
  519. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURENDTIME], PT_LONG);
  520. sPropVal.Value.ul = 24 * 4096;
  521. lpIcalItem->lstMsgProps.push_back(sPropVal);
  522. }
  523. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRINGBASE], PT_SYSTIME);
  524. if (icalproperty_get_recurrenceid(icProp).is_date)
  525. UnixTimeToFileTime(icaltime_as_timet(icalproperty_get_recurrenceid (icProp)), &sPropVal.Value.ft);
  526. else
  527. UnixTimeToFileTime(ICalTimeTypeToLocal(icProp), &sPropVal.Value.ft);
  528. lpIcalItem->lstMsgProps.push_back(sPropVal);
  529. //RECURRENCE-ID is present only for exception
  530. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_ISEXCEPTION], PT_BOOLEAN);
  531. sPropVal.Value.b = true;
  532. lpIcalItem->lstMsgProps.push_back(sPropVal);
  533. return hrSuccess;
  534. }
  535. /**
  536. * Adds Static properties to mapi structure. These are properties that
  537. * do not directly depend on the event, but may depend on the ical
  538. * method.
  539. *
  540. * Function sets properties such as PROP_SIDEEFFECT, PROP_SENDASICAL, PROP_COMMONASSIGN
  541. *
  542. * @param[in] icMethod ical method
  543. * @param[in,out] lpIcalItem structure in which mapi properties are set
  544. * @return Always returns hrSuccess
  545. */
  546. HRESULT VConverter::HrAddStaticProps(icalproperty_method icMethod, icalitem *lpIcalItem)
  547. {
  548. HRESULT hr = hrSuccess;
  549. SPropValue sPropVal;
  550. // From [MS-OXOCAL].pdf: All Calendar objects SHOULD include the following flags:
  551. sPropVal.Value.ul = seOpenToDelete | seOpenToCopy | seOpenToMove | seCoerceToInbox | seOpenForCtxMenu;
  552. // 1 20 40 10 100 == 171
  553. if(icMethod == ICAL_METHOD_REPLY || icMethod == ICAL_METHOD_REQUEST || icMethod == ICAL_METHOD_CANCEL)
  554. {
  555. // 400 | 800 | 1000 == 1c00 -> 1d71 but 1c61 should be set (because outlook says so)
  556. sPropVal.Value.ul |= seCannotUndoDelete | seCannotUndoCopy | seCannotUndoMove;
  557. // thus disable coercetoinbox, openforctxmenu .. which outlook does aswell.
  558. sPropVal.Value.ul &= ~(seCoerceToInbox | seOpenForCtxMenu);
  559. }
  560. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_SIDEEFFECT], PT_LONG);
  561. lpIcalItem->lstMsgProps.push_back(sPropVal);
  562. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_SENDASICAL], PT_BOOLEAN);
  563. sPropVal.Value.b = 1;
  564. lpIcalItem->lstMsgProps.push_back(sPropVal);
  565. // Needed for deleting an occurrence of a recurring item in outlook
  566. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_COMMONASSIGN], PT_LONG);
  567. sPropVal.Value.ul = 0;
  568. lpIcalItem->lstMsgProps.push_back(sPropVal);
  569. return hr;
  570. }
  571. /**
  572. * Add Simple properties to mapi structure from ical data. These are
  573. * properties that map 1:1 from Ical to MAPI and do not need
  574. * complicated calculations.
  575. *
  576. * Function sets summary, description, location, priority, private, sensitivity properties.
  577. *
  578. * @param[in] lpicEvent ical component containing the properties
  579. * @param[in,out] lpIcalItem mapi structure in which properties are set
  580. * @return Always returns hrSuccess
  581. */
  582. HRESULT VConverter::HrAddSimpleHeaders(icalcomponent *lpicEvent, icalitem *lpIcalItem)
  583. {
  584. HRESULT hr = hrSuccess;
  585. SPropValue sPropVal;
  586. icalproperty *lpicProp = NULL;
  587. int lPriority;
  588. int lClass = 0;
  589. std::string strClass;
  590. // Set subject / SUMMARY
  591. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_SUMMARY_PROPERTY);
  592. if (lpicProp){
  593. sPropVal.ulPropTag = PR_SUBJECT_W;
  594. hr = HrCopyString(m_converter, m_strCharset, lpIcalItem->base, icalcomponent_get_summary(lpicEvent), &sPropVal.Value.lpszW);
  595. if (hr != hrSuccess)
  596. sPropVal.Value.lpszW = const_cast<wchar_t *>(L"");
  597. lpIcalItem->lstMsgProps.push_back(sPropVal);
  598. } else {
  599. lpIcalItem->lstDelPropTags.push_back(PR_SUBJECT);
  600. }
  601. // Set body / DESCRIPTION
  602. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_DESCRIPTION_PROPERTY);
  603. if (!lpicProp)
  604. // used by exchange on replies in meeting requests
  605. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_COMMENT_PROPERTY);
  606. if (lpicProp){
  607. sPropVal.ulPropTag = PR_BODY_W;
  608. hr = HrCopyString(m_converter, m_strCharset, lpIcalItem->base, icalproperty_get_description(lpicProp), &sPropVal.Value.lpszW);
  609. if (hr != hrSuccess)
  610. sPropVal.Value.lpszW = const_cast<wchar_t *>(L"");
  611. lpIcalItem->lstMsgProps.push_back(sPropVal);
  612. } else {
  613. lpIcalItem->lstDelPropTags.push_back(PR_BODY_W);
  614. }
  615. // Set location / LOCATION
  616. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_LOCATION_PROPERTY);
  617. if (lpicProp) {
  618. hr = HrCopyString(m_converter, m_strCharset, lpIcalItem->base, icalproperty_get_location(lpicProp), &sPropVal.Value.lpszW);
  619. if (hr != hrSuccess)
  620. sPropVal.Value.lpszW = const_cast<wchar_t *>(L"");
  621. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_LOCATION], PT_UNICODE);
  622. lpIcalItem->lstMsgProps.push_back(sPropVal);
  623. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MEETINGLOCATION], PT_UNICODE);
  624. lpIcalItem->lstMsgProps.push_back(sPropVal);
  625. } else {
  626. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_LOCATION], PT_UNICODE));
  627. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MEETINGLOCATION], PT_UNICODE));
  628. }
  629. // Set importance and priority / PRIORITY
  630. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_PRIORITY_PROPERTY);
  631. if (lpicProp) {
  632. lPriority = icalproperty_get_priority(lpicProp);
  633. // @todo: test input and output!
  634. if (lPriority == 0)
  635. ;
  636. else if (lPriority < 5)
  637. lPriority = 1;
  638. else if (lPriority > 5)
  639. lPriority = -1;
  640. else
  641. lPriority = 0;
  642. sPropVal.ulPropTag = PR_IMPORTANCE;
  643. sPropVal.Value.ul = lPriority + 1;
  644. lpIcalItem->lstMsgProps.push_back(sPropVal);
  645. sPropVal.ulPropTag = PR_PRIORITY;
  646. sPropVal.Value.l = lPriority;
  647. lpIcalItem->lstMsgProps.push_back(sPropVal);
  648. } else {
  649. lpIcalItem->lstDelPropTags.push_back(PR_IMPORTANCE);
  650. lpIcalItem->lstDelPropTags.push_back(PR_PRIORITY);
  651. }
  652. // Private
  653. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_CLASS_PROPERTY);
  654. if (lpicProp){
  655. lClass = icalproperty_get_class(lpicProp);
  656. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_PRIVATE], PT_BOOLEAN);
  657. sPropVal.Value.b = (lClass == ICAL_CLASS_PRIVATE);
  658. lpIcalItem->lstMsgProps.push_back(sPropVal);
  659. }
  660. // Sensitivity, from same class property
  661. sPropVal.ulPropTag = PR_SENSITIVITY;
  662. if (lClass == ICAL_CLASS_PRIVATE)
  663. sPropVal.Value.ul = 2; // Private
  664. else if (lClass == ICAL_CLASS_CONFIDENTIAL)
  665. sPropVal.Value.ul = 3; // CompanyConfidential
  666. else
  667. sPropVal.Value.ul = 0; // Public
  668. lpIcalItem->lstMsgProps.push_back(sPropVal);
  669. // hr not used with goto exit, always return success
  670. return hrSuccess;
  671. }
  672. /**
  673. * Sets busy status in mapi property from ical.
  674. *
  675. * @param[in] lpicEvent ical VEVENT component
  676. * @param[in] icMethod ical method (eg. REPLY, REQUEST)
  677. * @param[out] lpIcalItem icalitem in which mapi propertry is set
  678. * @return MAPI error code
  679. */
  680. HRESULT VConverter::HrAddBusyStatus(icalcomponent *lpicEvent, icalproperty_method icMethod, icalitem *lpIcalItem)
  681. {
  682. HRESULT hr = hrSuccess;
  683. SPropValue sPropVal;
  684. icalproperty* lpicProp = NULL;
  685. // default: busy
  686. // 0: free
  687. // 1: tentative
  688. // 2: busy
  689. // 3: oof
  690. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_BUSYSTATUS], PT_LONG);
  691. // defaults if the TRANSP property is missing
  692. if (icMethod == ICAL_METHOD_CANCEL)
  693. sPropVal.Value.ul = 0;
  694. else
  695. sPropVal.Value.ul = 2;
  696. // caldav clients only uses the TRANSP property to set FreeBusy
  697. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_TRANSP_PROPERTY);
  698. if (lpicProp) {
  699. switch (icalproperty_get_transp(lpicProp)) {
  700. case ICAL_TRANSP_TRANSPARENT: // free
  701. case ICAL_TRANSP_TRANSPARENTNOCONFLICT:
  702. sPropVal.Value.ul = 0;
  703. break;
  704. case ICAL_TRANSP_X:
  705. case ICAL_TRANSP_OPAQUE: // busy
  706. case ICAL_TRANSP_OPAQUENOCONFLICT:
  707. case ICAL_TRANSP_NONE:
  708. sPropVal.Value.ul = 2;
  709. break;
  710. }
  711. }
  712. // Only process for Meeting Req from dagent
  713. if((m_bNoRecipients && icMethod == ICAL_METHOD_REQUEST) || m_ulUserStatus == 5) {
  714. // Meeting requests always have a BusyStatus of 1 (tentative), since this is the status of
  715. // the meeting which will be placed in your calendar when it has been processed but not accepted
  716. // The busy status of meeting responses is less important but seems to be 2 (Busy) in Outlook.
  717. // If the attendee is editing the entry through caldav then if the PARTSTAT param is NEEDS-ACTION
  718. // then the meeting is marked as tentative.
  719. sPropVal.Value.ul = 1;
  720. }
  721. lpIcalItem->lstMsgProps.push_back(sPropVal);
  722. // save fbstatus in icalitem
  723. lpIcalItem->ulFbStatus = sPropVal.Value.ul;
  724. if (icMethod == ICAL_METHOD_REPLY) {
  725. // @note the documentation doesn't explain the -1 on replies, but
  726. // makes sense in the case that it shouldn't be used.
  727. sPropVal.Value.ul = -1;
  728. } else {
  729. // X-MICROSOFT-CDO-INTENDEDBUSYSTATUS is used to set IntendedBusyStatus
  730. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_X_PROPERTY);
  731. while (lpicProp) {
  732. // X-MICROSOFT-CDO-INTENDEDBUSYSTATUS:FREE
  733. if (strcmp(icalproperty_get_x_name(lpicProp), "X-MICROSOFT-CDO-INTENDEDSTATUS") != 0) {
  734. lpicProp = icalcomponent_get_next_property(lpicEvent, ICAL_X_PROPERTY);
  735. continue;
  736. }
  737. const char *lpVal = icalproperty_get_x(lpicProp);
  738. if (lpVal == NULL)
  739. sPropVal.Value.ul = 2; /* like else case */
  740. else if (strcmp(lpVal, "FREE") == 0)
  741. sPropVal.Value.ul = 0;
  742. else if (strcmp(lpVal, "TENTATIVE") == 0)
  743. sPropVal.Value.ul = 1;
  744. else if(strcmp(lpVal, "BUSY") == 0)
  745. sPropVal.Value.ul = 2;
  746. else if (strcmp(lpVal, "OOF") == 0)
  747. sPropVal.Value.ul = 3;
  748. else
  749. sPropVal.Value.ul = 2;
  750. break;
  751. }
  752. // if the value wasn't updated, it still contains the PROP_INTENDEDBUSYSTATUS value, which is what we want.
  753. }
  754. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_INTENDEDBUSYSTATUS], PT_LONG);
  755. lpIcalItem->lstMsgProps.push_back(sPropVal);
  756. return hr;
  757. }
  758. /**
  759. * Set X ical properties in mapi properties
  760. *
  761. * @param[in] lpicEvent ical component to search x ical properties
  762. * @param[out] lpIcalItem icalitem struture to store mapi properties
  763. * @return Always returns hrSuccess
  764. */
  765. HRESULT VConverter::HrAddXHeaders(icalcomponent *lpicEvent, icalitem *lpIcalItem)
  766. {
  767. HRESULT hr = hrSuccess;
  768. SPropValue sPropVal;
  769. icalproperty* lpicProp = NULL;
  770. icalvalue *lpicValue = NULL;
  771. time_t ttCritcalChange = 0;
  772. int ulMaxCounter = 0;
  773. bool bHaveCounter = false;
  774. bool bOwnerApptID = false;
  775. bool bMozGen = false;
  776. // @todo: maybe save/restore headers to get "original" ical again?
  777. // add X-MICROSOFT-CDO & X-MOZ properties
  778. for (auto lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_X_PROPERTY);
  779. lpicProp != nullptr;
  780. lpicProp = icalcomponent_get_next_property(lpicEvent, ICAL_X_PROPERTY))
  781. {
  782. if (strcmp(icalproperty_get_x_name(lpicProp), "X-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE") == 0){
  783. lpicValue = icalvalue_new_from_string(ICAL_DATETIME_VALUE, icalproperty_get_x(lpicProp));
  784. if (lpicValue == nullptr)
  785. continue;
  786. ttCritcalChange = icaltime_as_timet_with_zone(icalvalue_get_datetime(lpicValue), NULL); // no timezone
  787. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_ATTENDEECRITICALCHANGE], PT_SYSTIME);
  788. UnixTimeToFileTime(ttCritcalChange, &sPropVal.Value.ft);
  789. lpIcalItem->lstMsgProps.push_back(sPropVal);
  790. icalvalue_free(lpicValue);
  791. }else if (strcmp(icalproperty_get_x_name(lpicProp), "X-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE") == 0){
  792. lpicValue = icalvalue_new_from_string(ICAL_DATETIME_VALUE, icalproperty_get_x(lpicProp));
  793. if (lpicValue == nullptr)
  794. continue;
  795. ttCritcalChange = icaltime_as_timet_with_zone(icalvalue_get_datetime(lpicValue), NULL); // no timezone
  796. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_OWNERCRITICALCHANGE], PT_SYSTIME);
  797. UnixTimeToFileTime(ttCritcalChange, &sPropVal.Value.ft);
  798. lpIcalItem->lstMsgProps.push_back(sPropVal);
  799. icalvalue_free(lpicValue);
  800. }else if (strcmp(icalproperty_get_x_name(lpicProp), "X-MICROSOFT-CDO-OWNERAPPTID") == 0){
  801. lpicValue = icalvalue_new_from_string(ICAL_INTEGER_VALUE, icalproperty_get_x(lpicProp));
  802. if (lpicValue == nullptr)
  803. continue;
  804. sPropVal.ulPropTag = PR_OWNER_APPT_ID;
  805. sPropVal.Value.ul = icalvalue_get_integer(lpicValue);
  806. lpIcalItem->lstMsgProps.push_back(sPropVal);
  807. bOwnerApptID = true;
  808. icalvalue_free(lpicValue);
  809. }else if (strcmp(icalproperty_get_x_name(lpicProp), "X-MICROSOFT-CDO-APPT-SEQUENCE") == 0){
  810. lpicValue = icalvalue_new_from_string(ICAL_INTEGER_VALUE, icalproperty_get_x(lpicProp));
  811. if (lpicValue == nullptr)
  812. continue;
  813. ulMaxCounter = std::max(ulMaxCounter, icalvalue_get_integer(lpicValue));
  814. bHaveCounter = true;
  815. icalvalue_free(lpicValue);
  816. } else if (strcmp(icalproperty_get_x_name(lpicProp), "X-MOZ-GENERATION") == 0) {
  817. lpicValue = icalvalue_new_from_string(ICAL_INTEGER_VALUE, icalproperty_get_x(lpicProp));
  818. if (lpicValue == nullptr)
  819. continue;
  820. ulMaxCounter = std::max(ulMaxCounter, icalvalue_get_integer(lpicValue));
  821. bHaveCounter = bMozGen = true;
  822. icalvalue_free(lpicValue);
  823. } else if (strcmp(icalproperty_get_x_name(lpicProp), "X-MOZ-SEND-INVITATIONS") == 0) {
  824. lpicValue = icalvalue_new_from_string(ICAL_X_VALUE, icalproperty_get_x(lpicProp));
  825. if (lpicValue == nullptr)
  826. continue;
  827. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZSENDINVITE], PT_BOOLEAN);
  828. const char *x = icalvalue_get_x(lpicValue);
  829. if (x == NULL)
  830. x = "";
  831. sPropVal.Value.b = strcmp(x, "TRUE") ? 0 : 1;
  832. lpIcalItem->lstMsgProps.push_back(sPropVal);
  833. icalvalue_free(lpicValue);
  834. }
  835. }
  836. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_SEQUENCE_PROPERTY);
  837. if (lpicProp) {
  838. ulMaxCounter = std::max(ulMaxCounter, icalcomponent_get_sequence(lpicEvent));
  839. bHaveCounter = true;
  840. }
  841. // Add ApptSequenceNo only if its present in the ical data, see #6116
  842. if (bHaveCounter) {
  843. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_APPTSEQNR], PT_LONG);
  844. sPropVal.Value.ul = ulMaxCounter;
  845. lpIcalItem->lstMsgProps.push_back(sPropVal);
  846. }
  847. if (!bOwnerApptID) {
  848. sPropVal.ulPropTag = PR_OWNER_APPT_ID;
  849. sPropVal.Value.ul = -1;
  850. lpIcalItem->lstMsgProps.push_back(sPropVal);
  851. }
  852. if (bMozGen) {
  853. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZGEN], PT_LONG);
  854. sPropVal.Value.ul = ulMaxCounter;
  855. lpIcalItem->lstMsgProps.push_back(sPropVal);
  856. }
  857. return hr;
  858. }
  859. /**
  860. * Sets Categories in mapi structure from ical data
  861. *
  862. * @param[in] lpicEvent ical component containing ical data
  863. * @param[in] lpIcalItem mapi structure in which the properties are set
  864. * @return Always returns hrSuccess
  865. */
  866. HRESULT VConverter::HrAddCategories(icalcomponent *lpicEvent, icalitem *lpIcalItem)
  867. {
  868. SPropValue sPropVal;
  869. icalproperty *lpicProp = NULL;
  870. const char* lpszCategories = NULL;
  871. std::vector<std::string> vCategories;
  872. int i;
  873. // Set keywords / CATEGORIES
  874. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_CATEGORIES_PROPERTY);
  875. if (!lpicProp) {
  876. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_KEYWORDS], PT_MV_STRING8));
  877. return hrSuccess;
  878. }
  879. while (lpicProp != NULL && (lpszCategories = icalproperty_get_categories(lpicProp)) != NULL) {
  880. vCategories.push_back(lpszCategories);
  881. lpicProp = icalcomponent_get_next_property(lpicEvent, ICAL_CATEGORIES_PROPERTY);
  882. }
  883. HRESULT hr = MAPIAllocateMore(vCategories.size() * sizeof(LPSTR),
  884. lpIcalItem->base, reinterpret_cast<void **>(&sPropVal.Value.MVszA.lppszA));
  885. if (hr != hrSuccess)
  886. return hr;
  887. i = 0;
  888. for (const auto &cat : vCategories) {
  889. int length = cat.length() + 1;
  890. hr = MAPIAllocateMore(length, lpIcalItem->base, (void **) &sPropVal.Value.MVszA.lppszA[i]);
  891. if (hr != hrSuccess)
  892. return hr;
  893. memcpy(sPropVal.Value.MVszA.lppszA[i++], cat.c_str(), length);
  894. }
  895. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_KEYWORDS], PT_MV_STRING8);
  896. sPropVal.Value.MVszA.cValues = vCategories.size();
  897. lpIcalItem->lstMsgProps.push_back(sPropVal);
  898. return hrSuccess;
  899. }
  900. /**
  901. * Set PR_SENT_REPRESENTING_* and PR_SENDER_* properties to mapi object.
  902. *
  903. * @param[in] lpIcalItem Use base pointer from here for allocations
  904. * @param[in] lplstMsgProps add generated properties to this list
  905. * @param[in] strEmail email address of the organizer
  906. * @param[in] strName full name of the organizer
  907. * @param[in] strType SMTP or ZARAFA
  908. * @param[in] cbEntryID bytes in entryid
  909. * @param[in] lpEntryID entryid describing organizer
  910. *
  911. * @return MAPI Error code
  912. */
  913. HRESULT VConverter::HrAddOrganizer(icalitem *lpIcalItem, std::list<SPropValue> *lplstMsgProps, const std::wstring &strEmail, const std::wstring &strName, const std::string &strType, ULONG cbEntryID, LPENTRYID lpEntryID)
  914. {
  915. SPropValue sPropVal;
  916. auto strSearchKey = strToUpper(strType + ":" + m_converter.convert_to<std::string>(strEmail));
  917. sPropVal.ulPropTag = PR_SENDER_ADDRTYPE_W;
  918. HRESULT hr = HrCopyString(m_converter, m_strCharset, lpIcalItem->base,
  919. strType.c_str(), &sPropVal.Value.lpszW);
  920. if (hr != hrSuccess)
  921. return hr;
  922. lplstMsgProps->push_back(sPropVal);
  923. sPropVal.ulPropTag = PR_SENT_REPRESENTING_ADDRTYPE;
  924. lplstMsgProps->push_back(sPropVal);
  925. sPropVal.ulPropTag = PR_SENDER_EMAIL_ADDRESS_W;
  926. hr = HrCopyString(lpIcalItem->base, strEmail.c_str(), &sPropVal.Value.lpszW);
  927. if (hr != hrSuccess)
  928. return hr;
  929. lplstMsgProps->push_back(sPropVal);
  930. sPropVal.ulPropTag = PR_SENT_REPRESENTING_EMAIL_ADDRESS;
  931. lplstMsgProps->push_back(sPropVal);
  932. sPropVal.ulPropTag = PR_SENDER_NAME_W;
  933. hr = HrCopyString(lpIcalItem->base, strName.c_str(), &sPropVal.Value.lpszW);
  934. if (hr != hrSuccess)
  935. return hr;
  936. lplstMsgProps->push_back(sPropVal);
  937. sPropVal.ulPropTag = PR_SENT_REPRESENTING_NAME;
  938. lplstMsgProps->push_back(sPropVal);
  939. sPropVal.ulPropTag = PR_SENDER_SEARCH_KEY;
  940. hr = Util::HrCopyBinary(strSearchKey.length() + 1, (LPBYTE)strSearchKey.c_str(), &sPropVal.Value.bin.cb, &sPropVal.Value.bin.lpb, lpIcalItem->base);
  941. if (hr != hrSuccess)
  942. return hr;
  943. lplstMsgProps->push_back(sPropVal);
  944. sPropVal.ulPropTag = PR_SENT_REPRESENTING_SEARCH_KEY;
  945. lplstMsgProps->push_back(sPropVal);
  946. // re-allocate memory to list with lpIcalItem
  947. hr = Util::HrCopyBinary(cbEntryID, (LPBYTE)lpEntryID, &sPropVal.Value.bin.cb, &sPropVal.Value.bin.lpb, lpIcalItem->base);
  948. if (hr != hrSuccess)
  949. return hr;
  950. sPropVal.ulPropTag = PR_SENDER_ENTRYID;
  951. lplstMsgProps->push_back(sPropVal);
  952. sPropVal.ulPropTag = PR_SENT_REPRESENTING_ENTRYID;
  953. lplstMsgProps->push_back(sPropVal);
  954. return hrSuccess;
  955. }
  956. /**
  957. * Sets Recipients in mapi structure from the ical data
  958. *
  959. * @param[in] lpicEvent ical component containing ical data
  960. * @param[in,out] lpIcalItem mapi structure in which the properties are set
  961. * @param[in,out] lplstMsgProps List in which of mapi properties set
  962. * @param[out] lplstIcalRecip List containing mapi recipients
  963. * @return MAPI error code
  964. */
  965. HRESULT VConverter::HrAddRecipients(icalcomponent *lpicEvent, icalitem *lpIcalItem, std::list<SPropValue> *lplstMsgProps, std::list<icalrecip> *lplstIcalRecip)
  966. {
  967. HRESULT hr = hrSuccess;
  968. std::wstring strEmail, strName;
  969. std::string strType;
  970. icalproperty *lpicProp = NULL;
  971. icalparameter *lpicParam = NULL;
  972. icalrecip icrAttendee = {0};
  973. ULONG cbEntryID = 0;
  974. LPENTRYID lpEntryID = NULL;
  975. ULONG cbEntryIDOneOff = 0;
  976. memory_ptr<ENTRYID> lpEntryIDOneOff;
  977. memory_ptr<SPropValue> lpsPropVal;
  978. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_ORGANIZER_PROPERTY);
  979. if (lpicProp) {
  980. const char *tmp = icalproperty_get_organizer(lpicProp);
  981. strEmail = m_converter.convert_to<wstring>(tmp, rawsize(tmp), m_strCharset.c_str());
  982. if (wcsncasecmp(strEmail.c_str(), L"mailto:", 7) == 0)
  983. strEmail = strEmail.erase(0, 7);
  984. lpicParam = icalproperty_get_first_parameter(lpicProp, ICAL_CN_PARAMETER);
  985. tmp = icalparameter_get_cn(lpicParam);
  986. if (lpicParam != NULL)
  987. strName = m_converter.convert_to<wstring>(tmp, rawsize(tmp), m_strCharset.c_str());
  988. else
  989. strName = strEmail; // set email as name OL does not display organiser name if not set.
  990. if (bIsUserLoggedIn(strEmail)) {
  991. static constexpr const SizedSPropTagArray(4, sPropTags) =
  992. {4, {PR_SMTP_ADDRESS_W, PR_DISPLAY_NAME_W, PR_ADDRTYPE_A, PR_ENTRYID}};
  993. ULONG count;
  994. hr = m_lpMailUser->GetProps(sPropTags, 0, &count, &~lpsPropVal);
  995. if (hr != hrSuccess)
  996. return hr;
  997. if (lpsPropVal[0].ulPropTag == PR_SMTP_ADDRESS_W)
  998. strEmail = lpsPropVal[0].Value.lpszW;
  999. if (lpsPropVal[1].ulPropTag == PR_DISPLAY_NAME_W)
  1000. strName = lpsPropVal[1].Value.lpszW;
  1001. if (lpsPropVal[2].ulPropTag == PR_ADDRTYPE_A)
  1002. strType = lpsPropVal[2].Value.lpszA;
  1003. if (lpsPropVal[3].ulPropTag == PR_ENTRYID) {
  1004. cbEntryID = lpsPropVal[3].Value.bin.cb;
  1005. lpEntryID = (LPENTRYID)lpsPropVal[3].Value.bin.lpb;
  1006. }
  1007. } else {
  1008. strType = "SMTP";
  1009. hr = ECCreateOneOff((LPTSTR)strName.c_str(), (LPTSTR)L"SMTP", (LPTSTR)strEmail.c_str(), MAPI_UNICODE, &cbEntryIDOneOff, &~lpEntryIDOneOff);
  1010. if (hr != hrSuccess)
  1011. return hr;
  1012. cbEntryID = cbEntryIDOneOff;
  1013. lpEntryID = lpEntryIDOneOff;
  1014. }
  1015. // add the organiser to the recipient list
  1016. hr = MAPIAllocateMore(cbEntryID, lpIcalItem->base, (void**)&icrAttendee.lpEntryID);
  1017. if (hr != hrSuccess)
  1018. return hr;
  1019. memcpy(icrAttendee.lpEntryID, lpEntryID, cbEntryID);
  1020. icrAttendee.cbEntryID = cbEntryID;
  1021. icrAttendee.strEmail = strEmail;
  1022. icrAttendee.strName = strName;
  1023. icrAttendee.ulRecipientType = MAPI_ORIG;
  1024. icrAttendee.ulTrackStatus = 0;
  1025. lplstIcalRecip->push_back(icrAttendee);
  1026. // The DAgent does not want these properties from ical, since it writes them itself
  1027. if (!m_bNoRecipients)
  1028. hr = HrAddOrganizer(lpIcalItem, lplstMsgProps, strEmail, strName, strType, cbEntryID, lpEntryID);
  1029. } else if (!m_bNoRecipients && m_lpMailUser) {
  1030. // single item from caldav without organizer, no need to set recipients, only organizer to self
  1031. static constexpr const SizedSPropTagArray(4, sPropTags) =
  1032. {4, {PR_SMTP_ADDRESS_W, PR_DISPLAY_NAME_W, PR_ADDRTYPE_A, PR_ENTRYID}};
  1033. ULONG count;
  1034. hr = m_lpMailUser->GetProps(sPropTags, 0, &count, &~lpsPropVal);
  1035. if (hr != hrSuccess)
  1036. return hr;
  1037. if (lpsPropVal[0].ulPropTag == PR_SMTP_ADDRESS_W)
  1038. strEmail = lpsPropVal[0].Value.lpszW;
  1039. if (lpsPropVal[1].ulPropTag == PR_DISPLAY_NAME_W)
  1040. strName = lpsPropVal[1].Value.lpszW;
  1041. if (lpsPropVal[2].ulPropTag == PR_ADDRTYPE_A)
  1042. strType = lpsPropVal[2].Value.lpszA;
  1043. if (lpsPropVal[3].ulPropTag == PR_ENTRYID) {
  1044. cbEntryID = lpsPropVal[3].Value.bin.cb;
  1045. lpEntryID = (LPENTRYID)lpsPropVal[3].Value.bin.lpb;
  1046. }
  1047. hr = HrAddOrganizer(lpIcalItem, lplstMsgProps, strEmail, strName, strType, cbEntryID, lpEntryID);
  1048. }
  1049. if (hr != hrSuccess)
  1050. return hr;
  1051. for (lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_ATTENDEE_PROPERTY);
  1052. lpicProp != NULL;
  1053. lpicProp = icalcomponent_get_next_property(lpicEvent, ICAL_ATTENDEE_PROPERTY))
  1054. {
  1055. const char *tmp = icalproperty_get_attendee(lpicProp);
  1056. /* Temporary fix for #7740:
  1057. * Newer libical already fixed the problem where "invalid" parameters let libical return a NULL pointer here,
  1058. * but since libical svn is not binary backward compatible, we can't just upgrade the library, which we really should.
  1059. */
  1060. if (!tmp)
  1061. // unable to log error of missing attendee
  1062. continue;
  1063. icrAttendee.strEmail = m_converter.convert_to<wstring>(tmp, rawsize(tmp), m_strCharset.c_str());
  1064. if (wcsncasecmp(icrAttendee.strEmail.c_str(), L"mailto:", 7) == 0)
  1065. icrAttendee.strEmail.erase(0, 7);
  1066. // @todo: Add organiser details if required.
  1067. if(icrAttendee.strEmail == strEmail) // remove organiser from attendee list.
  1068. continue;
  1069. lpicParam = icalproperty_get_first_parameter(lpicProp, ICAL_CN_PARAMETER);
  1070. if (lpicParam) {
  1071. const char *lpszProp = icalparameter_get_cn(lpicParam);
  1072. icrAttendee.strName = m_converter.convert_to<std::wstring>(lpszProp, rawsize(lpszProp), m_strCharset.c_str());
  1073. } else
  1074. icrAttendee.strName = icrAttendee.strEmail;
  1075. lpicParam = icalproperty_get_first_parameter(lpicProp, ICAL_ROLE_PARAMETER);
  1076. if (!lpicParam) {
  1077. icrAttendee.ulRecipientType = MAPI_TO;
  1078. } else {
  1079. switch (icalparameter_get_role(lpicParam)) {
  1080. case ICAL_ROLE_OPTPARTICIPANT:
  1081. icrAttendee.ulRecipientType = MAPI_CC;
  1082. break;
  1083. case ICAL_ROLE_NONPARTICIPANT:
  1084. icrAttendee.ulRecipientType = MAPI_BCC;
  1085. break;
  1086. case ICAL_ROLE_REQPARTICIPANT:
  1087. default:
  1088. icrAttendee.ulRecipientType = MAPI_TO;
  1089. break;
  1090. }
  1091. }
  1092. lpicParam = icalproperty_get_first_parameter(lpicProp, ICAL_PARTSTAT_PARAMETER);
  1093. if (lpicParam) {
  1094. switch (icalparameter_get_partstat(lpicParam)) {
  1095. case ICAL_PARTSTAT_TENTATIVE:
  1096. icrAttendee.ulTrackStatus = 2;
  1097. break;
  1098. case ICAL_PARTSTAT_ACCEPTED:
  1099. icrAttendee.ulTrackStatus = 3;
  1100. break;
  1101. case ICAL_PARTSTAT_DECLINED:
  1102. icrAttendee.ulTrackStatus = 4;
  1103. break;
  1104. case ICAL_PARTSTAT_NEEDSACTION:
  1105. icrAttendee.ulTrackStatus = 5;
  1106. break;
  1107. default:
  1108. icrAttendee.ulTrackStatus = 0;
  1109. break;
  1110. }
  1111. }
  1112. lplstIcalRecip->push_back(icrAttendee);
  1113. }
  1114. return hrSuccess;
  1115. }
  1116. /**
  1117. * Set Recipients for REPLY
  1118. *
  1119. * @param[in] lpicEvent ical component containing ical properties
  1120. * @param[in,out] lpIcalItem mapi structure in which properties are set
  1121. * @return MAPI error code
  1122. */
  1123. HRESULT VConverter::HrAddReplyRecipients(icalcomponent *lpicEvent, icalitem *lpIcalItem)
  1124. {
  1125. HRESULT hr = hrSuccess;
  1126. wstring strEmail, strName;
  1127. icalproperty *lpicProp = NULL;
  1128. icalparameter *lpicParam = NULL;
  1129. icalrecip icrAttendee;
  1130. ULONG cbEntryID;
  1131. memory_ptr<ENTRYID> lpEntryID;
  1132. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_ORGANIZER_PROPERTY);
  1133. if (lpicProp) {
  1134. const char *lpszProp = icalproperty_get_organizer(lpicProp);
  1135. icrAttendee.strEmail = m_converter.convert_to<std::wstring>(lpszProp, rawsize(lpszProp), m_strCharset.c_str());
  1136. if (wcsncasecmp(icrAttendee.strEmail.c_str(), L"mailto:", 7) == 0)
  1137. icrAttendee.strEmail.erase(0, 7);
  1138. lpicParam = icalproperty_get_first_parameter(lpicProp, ICAL_CN_PARAMETER);
  1139. if (lpicParam != NULL) {
  1140. lpszProp = icalparameter_get_cn(lpicParam);
  1141. icrAttendee.strName = m_converter.convert_to<std::wstring>(lpszProp, rawsize(lpszProp), m_strCharset.c_str());
  1142. }
  1143. icrAttendee.ulRecipientType = MAPI_TO;
  1144. lpIcalItem->lstRecips.push_back(icrAttendee);
  1145. }
  1146. // The DAgent does not want these properties from ical, since it writes them itself
  1147. if (!m_bNoRecipients) {
  1148. // @todo: what if >1 attendee ?!?
  1149. //PR_SENDER = ATTENDEE
  1150. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_ATTENDEE_PROPERTY);
  1151. if (lpicProp) {
  1152. const char *lpszProp = icalproperty_get_attendee(lpicProp);
  1153. strEmail = m_converter.convert_to<std::wstring>(lpszProp, rawsize(lpszProp), m_strCharset.c_str());
  1154. if (wcsncasecmp(strEmail.c_str(), L"mailto:", 7) == 0)
  1155. strEmail.erase(0, 7);
  1156. lpicParam = icalproperty_get_first_parameter(lpicProp, ICAL_CN_PARAMETER);
  1157. if (lpicParam) {
  1158. lpszProp = icalparameter_get_cn(lpicParam);
  1159. strName = m_converter.convert_to<std::wstring>(lpszProp, rawsize(lpszProp), m_strCharset.c_str());
  1160. }
  1161. }
  1162. hr = ECCreateOneOff((LPTSTR)strName.c_str(), (LPTSTR)L"SMTP", (LPTSTR)strEmail.c_str(), MAPI_UNICODE, &cbEntryID, &~lpEntryID);
  1163. if (hr != hrSuccess)
  1164. return hr;
  1165. hr = HrAddOrganizer(lpIcalItem, &lpIcalItem->lstMsgProps, strEmail, strName, "SMTP", cbEntryID, lpEntryID);
  1166. if (hr != hrSuccess)
  1167. return hr;
  1168. }
  1169. return hrSuccess;
  1170. }
  1171. /**
  1172. * Sets reminder in mapi structure from ical data
  1173. *
  1174. * @param[in] lpicEventRoot Root VCALENDAR component
  1175. * @param[in] lpicEvent ical component containing the reminder
  1176. * @param[in,out] lpIcalItem Structure in which remiders are stored
  1177. * @return Always returns hrSuccess
  1178. */
  1179. HRESULT VConverter::HrAddReminder(icalcomponent *lpicEventRoot, icalcomponent *lpicEvent, icalitem *lpIcalItem)
  1180. {
  1181. SPropValue sPropVal;
  1182. SPropValue sPropMozAck;
  1183. icalcomponent *lpicAlarm = NULL;
  1184. LONG ulRemindBefore = 0;
  1185. time_t ttReminderTime = 0;
  1186. time_t ttReminderNext = 0;
  1187. time_t ttMozLastAck = 0;
  1188. time_t ttMozLastAckMax = 0;
  1189. bool bReminderSet = false;
  1190. bool bHasMozAck = false;
  1191. icalproperty* lpicDTStartProp = NULL;
  1192. icalproperty* lpicProp = NULL;
  1193. icalvalue *lpicValue = NULL;
  1194. std::string strSuffix;
  1195. lpicAlarm = icalcomponent_get_first_component(lpicEvent, ICAL_VALARM_COMPONENT);
  1196. if (lpicAlarm == NULL) {
  1197. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERSET], PT_BOOLEAN);
  1198. sPropVal.Value.b = false;
  1199. lpIcalItem->lstMsgProps.push_back(sPropVal);
  1200. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERTIME], PT_SYSTIME));
  1201. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERNEXTTIME], PT_SYSTIME));
  1202. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERMINUTESBEFORESTART], PT_LONG));
  1203. /* No alarms found, so we can safely exit here. */
  1204. return hrSuccess;
  1205. }
  1206. HRESULT hr = HrParseVAlarm(lpicAlarm, &ulRemindBefore, &ttReminderTime,
  1207. &bReminderSet);
  1208. if (hr != hrSuccess)
  1209. // just skip the reminder
  1210. return hrSuccess;
  1211. // Handle Sunbird's dismiss/snooze, see: https://wiki.mozilla.org/Calendar:Feature_Implementations:Alarms
  1212. // X-MOZ-SNOOZE-TIME-1231250400000000:20090107T132846Z
  1213. // X-MOZ-LASTACK:20090107T132846Z
  1214. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_X_PROPERTY);
  1215. while (lpicProp) {
  1216. if (strcmp(icalproperty_get_x_name(lpicProp), "X-MOZ-LASTACK") == 0){
  1217. lpicValue = icalvalue_new_from_string(ICAL_DATETIME_VALUE, icalproperty_get_x(lpicProp));
  1218. ttMozLastAck = icaltime_as_timet_with_zone(icalvalue_get_datetime(lpicValue), NULL);
  1219. if(ttMozLastAck > ttMozLastAckMax)//save max of X-MOZ-LAST-ACK if present twice.
  1220. ttMozLastAckMax = ttMozLastAck;
  1221. icalvalue_free(lpicValue);
  1222. bHasMozAck = true;
  1223. }
  1224. else if (strcmp(icalproperty_get_x_name(lpicProp), "X-MOZ-SNOOZE-TIME") == 0) {
  1225. // x properties always return a char* as value :(
  1226. lpicValue = icalvalue_new_from_string(ICAL_DATETIME_VALUE, icalproperty_get_x(lpicProp));
  1227. ttReminderNext = icaltime_as_timet_with_zone(icalvalue_get_datetime(lpicValue), NULL); // no timezone
  1228. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERNEXTTIME], PT_SYSTIME);
  1229. UnixTimeToFileTime(ttReminderNext, &sPropVal.Value.ft);
  1230. lpIcalItem->lstMsgProps.push_back(sPropVal);
  1231. // X-MOZ-SNOOZE-TIME-1231250400000000
  1232. strSuffix = icalproperty_get_x_name(lpicProp);
  1233. if(strSuffix.compare("X-MOZ-SNOOZE-TIME") != 0)
  1234. {
  1235. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZ_SNOOZE_SUFFIX], PT_SYSTIME);
  1236. strSuffix.erase(0, strlen("X-MOZ-SNOOZE-TIME-"));
  1237. strSuffix.erase(10); // ignoring trailing 6 zeros for hh:mm:ss
  1238. UnixTimeToFileTime(atoi(strSuffix.c_str()), &sPropVal.Value.ft);
  1239. lpIcalItem->lstMsgProps.push_back(sPropVal);
  1240. }
  1241. icalvalue_free(lpicValue);
  1242. } else if (strcmp(icalproperty_get_x_name(lpicProp), "X-MICROSOFT-RTF") == 0) {
  1243. lpicValue = icalvalue_new_from_string(ICAL_X_VALUE, icalproperty_get_x(lpicProp));
  1244. string rtf = base64_decode(icalvalue_get_x(lpicValue));
  1245. sPropVal.ulPropTag = PR_RTF_COMPRESSED;
  1246. sPropVal.Value.bin.cb = rtf.size();
  1247. hr = MAPIAllocateMore(sPropVal.Value.bin.cb,
  1248. lpIcalItem->base, reinterpret_cast<void **>(&sPropVal.Value.bin.lpb));
  1249. if (hr != hrSuccess)
  1250. return hr;
  1251. memcpy(sPropVal.Value.bin.lpb, (LPBYTE)rtf.c_str(), sPropVal.Value.bin.cb);
  1252. lpIcalItem->lstMsgProps.push_back(sPropVal);
  1253. icalvalue_free(lpicValue);
  1254. }
  1255. lpicProp = icalcomponent_get_next_property(lpicEvent, ICAL_X_PROPERTY);
  1256. }
  1257. if (bHasMozAck) { // save X-MOZ-LAST-ACK if found in request.
  1258. sPropMozAck.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZLASTACK], PT_SYSTIME);
  1259. UnixTimeToFileTime(ttMozLastAckMax, &sPropMozAck.Value.ft);
  1260. lpIcalItem->lstMsgProps.push_back(sPropMozAck);
  1261. }
  1262. else { //delete X-MOZ-LAST-ACK if not found in request.
  1263. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZLASTACK], PT_SYSTIME));
  1264. }
  1265. // reminderset
  1266. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERSET], PT_BOOLEAN);
  1267. sPropVal.Value.b = bReminderSet;
  1268. lpIcalItem->lstMsgProps.push_back(sPropVal);
  1269. // remindbefore
  1270. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERMINUTESBEFORESTART], PT_LONG);
  1271. sPropVal.Value.ul = ulRemindBefore;
  1272. lpIcalItem->lstMsgProps.push_back(sPropVal);
  1273. // remindertime
  1274. if (ttReminderTime == 0) {
  1275. // get starttime from item
  1276. // DTSTART must be available
  1277. lpicDTStartProp = icalcomponent_get_first_property(lpicEvent, ICAL_DTSTART_PROPERTY);
  1278. if (lpicDTStartProp == NULL)
  1279. return MAPI_E_INVALID_PARAMETER;
  1280. ttReminderTime = ICalTimeTypeToUTC(lpicEventRoot, lpicDTStartProp);
  1281. }
  1282. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERTIME], PT_SYSTIME);
  1283. UnixTimeToFileTime(ttReminderTime, &sPropVal.Value.ft);
  1284. lpIcalItem->lstMsgProps.push_back(sPropVal);
  1285. if(ttReminderNext == 0)
  1286. {
  1287. if (bReminderSet) {
  1288. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERNEXTTIME], PT_SYSTIME);
  1289. UnixTimeToFileTime(ttReminderTime - (ulRemindBefore * 60), &sPropVal.Value.ft);
  1290. lpIcalItem->lstMsgProps.push_back(sPropVal);
  1291. } else {
  1292. //delete the next-reminder time if X-MOZ-SNOOZE-TIME is absent and reminder is not set.
  1293. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERNEXTTIME], PT_SYSTIME));
  1294. }
  1295. }
  1296. return hrSuccess;
  1297. }
  1298. /**
  1299. * Adds recurrence to icalitem structure from ical data
  1300. *
  1301. * @param[in] lpicEventRoot Root VCALENDAR component
  1302. * @param[in] lpicEvent ical(VEVENT/VTODO) component being parsed
  1303. * @param[in] bIsAllday set times for normal or allday event
  1304. * @param[out] lpIcalItem icalitem structure in which the properties are set
  1305. * @return MAPI error code
  1306. * @retval MAPI_E_CORRUPT_DATA timezone is not set in ical data
  1307. * @retval MAPI_E_NOT_FOUND invalid recurrence is set
  1308. */
  1309. HRESULT VConverter::HrAddRecurrence(icalcomponent *lpicEventRoot, icalcomponent *lpicEvent, bool bIsAllday, icalitem *lpIcalItem)
  1310. {
  1311. ICalRecurrence icRecClass;
  1312. SPropValue spSpropVal = {0};
  1313. TIMEZONE_STRUCT zone;
  1314. HRESULT hr = hrSuccess;
  1315. icalproperty *lpicProp = icalcomponent_get_first_property(lpicEvent,
  1316. ICAL_RRULE_PROPERTY);
  1317. if (lpicProp == NULL) {
  1318. // set isRecurring to false , property required by BlackBerry.
  1319. spSpropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRING], PT_BOOLEAN);
  1320. spSpropVal.Value.b = false;
  1321. lpIcalItem->lstMsgProps.push_back(spSpropVal);
  1322. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRENCESTATE], PT_BINARY));
  1323. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRENCEPATTERN], PT_STRING8));
  1324. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRENCE_START], PT_SYSTIME));
  1325. lpIcalItem->lstDelPropTags.push_back(CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRENCE_END], PT_SYSTIME));
  1326. lpIcalItem->lpRecurrence = NULL;
  1327. // remove all exception attachments from existing message, done in ICal2Mapi.cpp
  1328. return hrSuccess;
  1329. }
  1330. if (!bIsAllday && m_iCurrentTimeZone == m_mapTimeZones->end()) {
  1331. // if we have an RRULE, we must have a timezone
  1332. return MAPI_E_CORRUPT_DATA;
  1333. } else if (m_iCurrentTimeZone == m_mapTimeZones->end()) {
  1334. hr = HrGetTzStruct("Etc/UTC", &zone);
  1335. if (hr != hrSuccess)
  1336. return hr;
  1337. } else {
  1338. zone = m_iCurrentTimeZone->second;
  1339. }
  1340. hr = icRecClass.HrParseICalRecurrenceRule(zone, lpicEventRoot,
  1341. lpicEvent, bIsAllday, m_lpNamedProps, lpIcalItem);
  1342. if (hr != hrSuccess)
  1343. return hr;
  1344. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_X_PROPERTY);
  1345. while (lpicProp) {
  1346. if (strcmp(icalproperty_get_x_name(lpicProp), "X-ZARAFA-REC-PATTERN") == 0 ||
  1347. strcmp(icalproperty_get_x_name(lpicProp), "X-KOPANO-REC-PATTERN") == 0) {
  1348. spSpropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRENCEPATTERN], PT_UNICODE);
  1349. HrCopyString(m_converter, m_strCharset, lpIcalItem->base, icalproperty_get_x(lpicProp), &spSpropVal.Value.lpszW);
  1350. lpIcalItem->lstMsgProps.push_back(spSpropVal);
  1351. }
  1352. lpicProp = icalcomponent_get_next_property(lpicEvent, ICAL_X_PROPERTY);
  1353. }
  1354. return hrSuccess;
  1355. }
  1356. /**
  1357. * Make a MAPI exception message, and add this to the previous parsed
  1358. * icalitem (which is the main ical item).
  1359. *
  1360. * @param[in] lpEventRoot The top ical event which is recurring
  1361. * @param[in] lpEvent The current ical event describing an exception for lpEventRoot
  1362. * @param[in] bIsAllday set times for normal or allday event
  1363. * @param[in,out] lpPrevItem The icalitem struct that contains the MAPI representation of this recurrent item
  1364. *
  1365. * @return MAPI error code
  1366. */
  1367. HRESULT VConverter::HrAddException(icalcomponent *lpEventRoot, icalcomponent *lpEvent, bool bIsAllday, icalitem *lpPrevItem)
  1368. {
  1369. HRESULT hr;
  1370. ICalRecurrence cRec;
  1371. icalitem::exception ex;
  1372. icalproperty_method icMethod = ICAL_METHOD_NONE;
  1373. hr = HrCompareUids(lpPrevItem, lpEvent);
  1374. if (hr != hrSuccess)
  1375. return hr;
  1376. if (lpPrevItem->lpRecurrence == NULL)
  1377. // can't add exceptions if the previous item did not have an RRULE
  1378. return MAPI_E_CORRUPT_DATA;
  1379. icMethod = icalcomponent_get_method(lpEventRoot);
  1380. // it's the same item, handle exception
  1381. hr = cRec.HrMakeMAPIException(lpEventRoot, lpEvent, lpPrevItem, bIsAllday, m_lpNamedProps, m_strCharset, &ex);
  1382. if (hr != hrSuccess)
  1383. return hr;
  1384. hr = HrAddRecipients(lpEvent, lpPrevItem, &ex.lstMsgProps, &ex.lstRecips);
  1385. if (hr != hrSuccess)
  1386. return hr;
  1387. hr = HrResolveUser(lpPrevItem->base, &ex.lstRecips);
  1388. if (hr != hrSuccess)
  1389. return hr;
  1390. hr = HrAddBaseProperties(icMethod, lpEvent, lpPrevItem->base, true, &ex.lstMsgProps);
  1391. if (hr != hrSuccess)
  1392. return hr;
  1393. lpPrevItem->lstExceptionAttachments.push_back(std::move(ex));
  1394. return hrSuccess;
  1395. }
  1396. /**
  1397. * Returns the ical timezone of a MAPI calendar message. When there is no timezone information, UTC will be used.
  1398. *
  1399. * @param[in] ulProps Number of properties in lpProps
  1400. * @param[in] lpProps All (required) properties of the MAPI message
  1401. * @param[out] lpstrTZid The name (unique id) of the timezone
  1402. * @param[out] lpTZinfo MAPI timezone struct
  1403. * @param[out] lppicTZinfo Ical timezone information
  1404. *
  1405. * @return MAPI error code
  1406. */
  1407. HRESULT VConverter::HrFindTimezone(ULONG ulProps, LPSPropValue lpProps, std::string *lpstrTZid, TIMEZONE_STRUCT *lpTZinfo, icaltimezone **lppicTZinfo)
  1408. {
  1409. HRESULT hr = hrSuccess;
  1410. string strTZid;
  1411. string::size_type pos;
  1412. TIMEZONE_STRUCT ttTZinfo = {0};
  1413. icaltimezone *lpicTZinfo = NULL;
  1414. icalcomponent *lpicComp = NULL;
  1415. size_t ulPos = 0;
  1416. // @todo if we ever encounter a timezone string with non-ascii characters, we need to move to std::wstring for the timezone string.
  1417. // but since I haven't seen this, I'll be lazy and do a convert to us-ascii strings.
  1418. // Retrieve timezone. If available (outlook fills this in for recurring items), place it in lpMapTimeZones
  1419. auto lpPropTimeZoneString = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TIMEZONE], PT_UNICODE));
  1420. if (lpPropTimeZoneString == NULL) {
  1421. // use all dates/times as UTC
  1422. strTZid = "(GMT+0000)";
  1423. auto lpProp = PCpropFindProp(lpProps, ulProps, PR_MESSAGE_CLASS_W);
  1424. if(lpProp && (wcscasecmp(lpProp->Value.lpszW, L"IPM.Task") == 0) && !m_mapTimeZones->empty())
  1425. {
  1426. m_iCurrentTimeZone = m_mapTimeZones->begin();
  1427. ttTZinfo = m_iCurrentTimeZone->second;
  1428. strTZid = m_iCurrentTimeZone->first;
  1429. }
  1430. else
  1431. goto done; // UTC is not placed in the map, and not placed in ical, so we're done here
  1432. } else
  1433. strTZid = m_converter.convert_to<std::string>(lpPropTimeZoneString->Value.lpszW);
  1434. if (strTZid.empty()) {
  1435. strTZid = "(GMT+0000)";
  1436. // UTC not in map, ttTZInfo still 0
  1437. goto done;
  1438. }
  1439. if (strTZid[0] == '(') {
  1440. // shorten string so the timezone id is not so long, and hopefully matches more for the same timezone
  1441. // from: (GMT+01:00) Amsterdam, Berlijn, Bern, Rome, Stockholm, Wenen
  1442. // from: (GMT+01.00) Sarajevo/Warsaw/Zagreb
  1443. // to: (GMT+01:00) (note: the dot is not converted .. should we?)
  1444. pos = strTZid.rfind(')');
  1445. strTZid.erase(pos+1);
  1446. }
  1447. ulPos = strTZid.find('+');
  1448. // check if strTZid is in map
  1449. m_iCurrentTimeZone = m_mapTimeZones->find(strTZid);
  1450. if (m_iCurrentTimeZone != m_mapTimeZones->end()) {
  1451. // already used this timezone before
  1452. ttTZinfo = m_iCurrentTimeZone->second;
  1453. } else {
  1454. auto lpPropTimeZoneStruct = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TIMEZONEDATA], PT_BINARY));
  1455. if (lpPropTimeZoneStruct && lpPropTimeZoneStruct->Value.bin.cb >= sizeof(TIMEZONE_STRUCT) && lpPropTimeZoneStruct->Value.bin.lpb) {
  1456. ttTZinfo = *(TIMEZONE_STRUCT*)lpPropTimeZoneStruct->Value.bin.lpb;
  1457. (*m_mapTimeZones)[strTZid] = ttTZinfo;
  1458. // keep timezone pointer for recurrence
  1459. m_iCurrentTimeZone = m_mapTimeZones->find(strTZid);
  1460. } else if(ulPos != string::npos){
  1461. strTZid = "(GMT+0000)"; // identify GMT+XXX timezones
  1462. goto done;
  1463. }
  1464. else {
  1465. strTZid = "(GMT-0000)"; // identify GMT-XXX timezones
  1466. goto done;
  1467. }
  1468. }
  1469. // construct ical version for icaltime_from_timet_with_zone()
  1470. hr = HrCreateVTimeZone(strTZid, ttTZinfo, &lpicComp);
  1471. if (hr == hrSuccess) {
  1472. lpicTZinfo = icaltimezone_new();
  1473. if (icaltimezone_set_component(lpicTZinfo, lpicComp) == 0) {
  1474. icalcomponent_free(lpicComp);
  1475. icaltimezone_free(lpicTZinfo, true);
  1476. }
  1477. }
  1478. hr = hrSuccess;
  1479. done:
  1480. *lpstrTZid = std::move(strTZid);
  1481. *lpTZinfo = ttTZinfo;
  1482. *lppicTZinfo = lpicTZinfo;
  1483. return hr;
  1484. }
  1485. HRESULT VConverter::HrSetTimeProperty(time_t tStamp, bool bDateOnly, icaltimezone *lpicTZinfo, const std::string &strTZid, icalproperty_kind icalkind, icalproperty *lpicProp)
  1486. {
  1487. HRESULT hr = hrSuccess;
  1488. icaltimetype ittStamp;
  1489. // if (bDateOnly && !lpicTZinfo)
  1490. /*
  1491. * ZCP-12962: Disregarding tzinfo. Even a minor miscalculation can
  1492. * cause a day shift; if possible, we should probably improve the
  1493. * actual calculation when we encounter such a problem.
  1494. */
  1495. if (bDateOnly) {
  1496. struct tm date;
  1497. // We have a problem now. This is a 'date' property type, so time information should not be sent. However,
  1498. // the timestamp in tStamp *does* have a time part, which is indicating the start of the day in GMT (so, this
  1499. // would be say 23:00 in central europe, and 03:00 in brasil). This means that if we 'just' take the date part
  1500. // of the timestamp, you will get the wrong day if you're east of GMT. Unfortunately, we don't know the
  1501. // timezone either, so we have to do some guesswork. What we do now is a 'round to closest date'. This will
  1502. // basically work for any timezone that has an offset between GMT+13 and GMT-10. So the 4th at 23:00 will become
  1503. // the 5h, and the 5th at 03:00 will become the 5th.
  1504. /* So this is a known problem for users in GMT+14, GMT-12 and
  1505. * GMT-11 (Kiribati, Samoa, ..). Sorry. Fortunately, there are
  1506. * not many people in these timezones. For this to work
  1507. * correctly, clients should store the correct timezone in the
  1508. * appointment (WebApp does not do this currently), and we need
  1509. * to consider timezones here again.
  1510. */
  1511. gmtime_r(&tStamp, &date);
  1512. if (date.tm_hour >= 11) {
  1513. // Move timestamp up one day so that later conversion to date-only will be correct
  1514. tStamp += 86400;
  1515. }
  1516. }
  1517. if (!bDateOnly && lpicTZinfo != NULL)
  1518. ittStamp = icaltime_from_timet_with_zone(tStamp, bDateOnly, lpicTZinfo);
  1519. else
  1520. ittStamp = icaltime_from_timet_with_zone(tStamp, bDateOnly, icaltimezone_get_utc_timezone());
  1521. icalproperty_set_value(lpicProp, icalvalue_new_datetime(ittStamp));
  1522. // only allowed to add timezone information on non-allday events
  1523. if (lpicTZinfo && !bDateOnly)
  1524. icalproperty_add_parameter(lpicProp, icalparameter_new_from_value_string(ICAL_TZID_PARAMETER, strTZid.c_str()));
  1525. return hr;
  1526. }
  1527. /**
  1528. * Converts the Unix timestamp to iCal information and adds a new iCal
  1529. * property to the given ical component.
  1530. *
  1531. * @param[in] tStamp The Unix timestamp value to set in the iCal property
  1532. * @param[in] bDateOnly true if only the date should be set (all day events) or false for full time conversion
  1533. * @param[in] lpicTZinfo Pointer to ical timezone for this property (required for recurring events). If NULL, UTC will be used.
  1534. * @param[in] strTZid Human readable name of the timezone
  1535. * @param[in] icalkind Kind of property the timestamp is describing
  1536. * @param[in,out] lpicEvent Ical property will be added to this event, when hrSuccess is returned.
  1537. *
  1538. * @return MAPI error code
  1539. */
  1540. HRESULT VConverter::HrSetTimeProperty(time_t tStamp, bool bDateOnly, icaltimezone *lpicTZinfo, const std::string &strTZid, icalproperty_kind icalkind, icalcomponent *lpicEvent)
  1541. {
  1542. icalproperty *lpicProp = icalproperty_new(icalkind);
  1543. if (lpicProp == NULL)
  1544. return MAPI_E_INVALID_PARAMETER;
  1545. HRESULT hr = HrSetTimeProperty(tStamp, bDateOnly, lpicTZinfo, strTZid,
  1546. icalkind, lpicProp);
  1547. icalcomponent_add_property(lpicEvent, lpicProp);
  1548. return hr;
  1549. }
  1550. /**
  1551. * Sets the Organizer (From) and Attendees (To and Cc) in the given
  1552. * ical event. It also determains the ical method for this event,
  1553. * since the method and attendees depend on the message class and the
  1554. * meeting status.
  1555. *
  1556. * Adds one or more of the following ical properties:
  1557. * - STATUS
  1558. * - ATTENDEE
  1559. * - ORGANIZER
  1560. * - X-MOZ-SEND-INVITATIONS
  1561. *
  1562. * @param[in] lpParentMsg The main message (different from lpMessage in case of exceptions)
  1563. * @param[in] lpMessage The main or exception message
  1564. * @param[in] ulProps Number of properties in lpProps
  1565. * @param[in] lpProps All (required) properties from lpMessage
  1566. * @param[in] lpicMethod The method for this ical event
  1567. * @param[in,out] lpicEvent This ical event will be modified
  1568. *
  1569. * @return MAPI error code
  1570. */
  1571. HRESULT VConverter::HrSetOrganizerAndAttendees(LPMESSAGE lpParentMsg, LPMESSAGE lpMessage, ULONG ulProps, LPSPropValue lpProps, icalproperty_method *lpicMethod, icalcomponent *lpicEvent)
  1572. {
  1573. HRESULT hr = hrSuccess;
  1574. icalproperty_method icMethod = ICAL_METHOD_NONE;
  1575. wstring strSenderName, strSenderType, strSenderEmailAddr;
  1576. wstring strReceiverName, strReceiverType, strReceiverEmailAddr;
  1577. wstring strRepsSenderName, strRepsSenderType, strRepsSenderEmailAddr;
  1578. object_ptr<IMAPITable> lpTable;
  1579. memory_ptr<SPropValue> lpSpropVal;
  1580. icalproperty *lpicProp = NULL;
  1581. icalparameter *lpicParam = NULL;
  1582. string strMessageClass;
  1583. wstring wstrBuf;
  1584. ULONG ulMeetingStatus = 0;
  1585. bool bCounterProposal = false;
  1586. auto lpPropVal = PCpropFindProp(lpProps, ulProps, m_lpNamedProps->aulPropTag[PROP_COUNTERPROPOSAL]);
  1587. if(lpPropVal && PROP_TYPE(lpPropVal->ulPropTag) == PT_BOOLEAN && lpPropVal->Value.b)
  1588. bCounterProposal = true;
  1589. //Remove Organiser & Attendees of Root event for exception.
  1590. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_ORGANIZER_PROPERTY);
  1591. if (lpicProp) {
  1592. icalcomponent_remove_property(lpicEvent, lpicProp);
  1593. icalproperty_free(lpicProp);
  1594. }
  1595. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_ATTENDEE_PROPERTY);
  1596. while (lpicProp) {
  1597. if (lpicProp) {
  1598. icalcomponent_remove_property(lpicEvent, lpicProp);
  1599. icalproperty_free(lpicProp);
  1600. }
  1601. lpicProp = icalcomponent_get_next_property(lpicEvent, ICAL_X_PROPERTY);
  1602. }
  1603. // PR_SENT_REPRESENTING_ENTRYID is the owner of the meeting.
  1604. // PR_SENDER_ENTRYID can be a delegate/owner
  1605. lpPropVal = PCpropFindProp(lpProps, ulProps, PR_SENT_REPRESENTING_ENTRYID);
  1606. if (lpPropVal) // ignore error
  1607. HrGetAddress(m_lpAdrBook, (LPENTRYID)lpPropVal->Value.bin.lpb, lpPropVal->Value.bin.cb, strRepsSenderName, strRepsSenderType, strRepsSenderEmailAddr);
  1608. // Request mail address from addressbook to get the actual email address
  1609. // use the parent message, OL does not set PR_SENDER_ENTRYID in exception message.
  1610. hr = HrGetAddress(m_lpAdrBook, lpParentMsg,
  1611. PR_SENDER_ENTRYID, PR_SENDER_NAME, PR_SENDER_ADDRTYPE, PR_SENDER_EMAIL_ADDRESS,
  1612. strSenderName, strSenderType, strSenderEmailAddr);
  1613. if (hr != hrSuccess)
  1614. return hr;
  1615. // get class to find method and type for attendees and organizer
  1616. lpPropVal = PCpropFindProp(lpProps, ulProps, PR_MESSAGE_CLASS_W);
  1617. if (lpPropVal == nullptr)
  1618. return MAPI_E_NOT_FOUND;
  1619. strMessageClass = m_converter.convert_to<std::string>(lpPropVal->Value.lpszW);
  1620. // Set attendee info
  1621. if (strMessageClass.compare(0, string("IPM.Schedule.Meeting.Resp.").length(), string("IPM.Schedule.Meeting.Resp.")) == 0)
  1622. {
  1623. // responding to a meeting request:
  1624. // the to should only be the organizer of this meeting
  1625. if(bCounterProposal)
  1626. icMethod = ICAL_METHOD_COUNTER;
  1627. else {
  1628. icMethod = ICAL_METHOD_REPLY;
  1629. // gmail always sets CONFIRMED, exchange fills in the correct value ... doesn't seem to matter .. for now
  1630. icalcomponent_add_property(lpicEvent, icalproperty_new_status(ICAL_STATUS_CONFIRMED));
  1631. }
  1632. if (strMessageClass.rfind("Pos") != string::npos)
  1633. lpicParam = icalparameter_new_partstat(ICAL_PARTSTAT_ACCEPTED);
  1634. else if (strMessageClass.rfind("Neg") != string::npos)
  1635. lpicParam = icalparameter_new_partstat(ICAL_PARTSTAT_DECLINED);
  1636. else if (strMessageClass.rfind("Tent") != string::npos)
  1637. lpicParam = icalparameter_new_partstat(ICAL_PARTSTAT_TENTATIVE);
  1638. else
  1639. // shouldn't happen, but better than having no lpicParam pointer
  1640. lpicParam = icalparameter_new_partstat(ICAL_PARTSTAT_ACCEPTED);
  1641. // I am the only attendee that is replying
  1642. wstrBuf = L"mailto:" + (strRepsSenderEmailAddr.empty() ? strSenderEmailAddr : strRepsSenderEmailAddr);
  1643. lpicProp = icalproperty_new_attendee(m_converter.convert_to<string>(wstrBuf).c_str());
  1644. icalproperty_add_parameter(lpicProp, lpicParam);
  1645. wstrBuf = strRepsSenderName.empty() ? strSenderName: strRepsSenderName;
  1646. if (!wstrBuf.empty())
  1647. icalproperty_add_parameter(lpicProp, icalparameter_new_cn(m_converter.convert_to<string>(m_strCharset.c_str(), wstrBuf, rawsize(wstrBuf), CHARSET_WCHAR).c_str()));
  1648. wstrBuf = L"mailto:" + strSenderEmailAddr;
  1649. if (!strSenderEmailAddr.empty() && strSenderEmailAddr != strRepsSenderEmailAddr)
  1650. icalproperty_add_parameter(lpicProp, icalparameter_new_sentby(m_converter.convert_to<string>(wstrBuf).c_str()));
  1651. icalcomponent_add_property(lpicEvent, lpicProp);
  1652. // Organizer should be the only MAPI_TO entry
  1653. hr = lpMessage->GetRecipientTable(MAPI_UNICODE, &~lpTable);
  1654. if (hr != hrSuccess)
  1655. return hr;
  1656. rowset_ptr lpRows;
  1657. hr = lpTable->QueryRows(-1, 0, &~lpRows);
  1658. if (hr != hrSuccess)
  1659. return hr;
  1660. // The response should only be sent to the organizer (@todo restrict on MAPI_TO ? ...)
  1661. if (lpRows->cRows != 1)
  1662. return MAPI_E_CALL_FAILED;
  1663. // @todo: use correct index number?
  1664. hr = HrGetAddress(m_lpAdrBook, lpRows->aRow[0].lpProps, lpRows->aRow[0].cValues,
  1665. PR_ENTRYID, PR_DISPLAY_NAME, PR_ADDRTYPE, PR_EMAIL_ADDRESS,
  1666. strReceiverName, strReceiverType, strReceiverEmailAddr);
  1667. if (hr != hrSuccess)
  1668. return hr;
  1669. wstrBuf = L"mailto:" + strReceiverEmailAddr;
  1670. lpicProp = icalproperty_new_organizer(m_converter.convert_to<string>(m_strCharset.c_str(), wstrBuf, rawsize(wstrBuf), CHARSET_WCHAR).c_str());
  1671. if (!strReceiverName.empty()) {
  1672. lpicParam = icalparameter_new_cn(m_converter.convert_to<string>(m_strCharset.c_str(), strReceiverName, rawsize(strReceiverName), CHARSET_WCHAR).c_str());
  1673. icalproperty_add_parameter(lpicProp, lpicParam);
  1674. }
  1675. icalcomponent_add_property(lpicEvent, lpicProp);
  1676. }
  1677. else
  1678. {
  1679. // strMessageClass == "IPM.Schedule.Meeting.Request", "IPM.Schedule.Meeting.Canceled" or ....?
  1680. // strMessageClass == "IPM.Appointment": normal calendar item
  1681. // If we're dealing with a meeting, preset status to 1. PROP_MEETINGSTATUS may not be set
  1682. if (strMessageClass.compare(0, string("IPM.Schedule.Meeting").length(), string("IPM.Schedule.Meeting")) == 0)
  1683. ulMeetingStatus = 1;
  1684. // a normal calendar item has meeting status == 0, all other types != 0
  1685. lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MEETINGSTATUS], PT_LONG));
  1686. if (lpPropVal)
  1687. ulMeetingStatus = lpPropVal->Value.ul;
  1688. else if (HrGetOneProp(lpParentMsg, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MEETINGSTATUS], PT_LONG), &~lpSpropVal) == hrSuccess)
  1689. // if MeetingStatus flag is not set in exception message, retrive it from parent message.
  1690. ulMeetingStatus = lpSpropVal->Value.ul;
  1691. // meeting bit enabled
  1692. if (ulMeetingStatus & 1) {
  1693. if (ulMeetingStatus & 4) {
  1694. icalcomponent_add_property(lpicEvent, icalproperty_new_status(ICAL_STATUS_CANCELLED));
  1695. icMethod = ICAL_METHOD_CANCEL;
  1696. } else {
  1697. icalcomponent_add_property(lpicEvent, icalproperty_new_status(ICAL_STATUS_CONFIRMED));
  1698. icMethod = ICAL_METHOD_REQUEST;
  1699. }
  1700. // meeting action, add all attendees, request reply when needed
  1701. hr = HrSetICalAttendees(lpMessage, strSenderEmailAddr, lpicEvent);
  1702. if (hr != hrSuccess)
  1703. return hr;
  1704. //Set this property to force thunderbird to send invitations mails.
  1705. lpPropVal = PCpropFindProp (lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZSENDINVITE], PT_BOOLEAN));
  1706. if (lpPropVal && !lpPropVal->Value.b)
  1707. lpicProp = icalproperty_new_x("FALSE");
  1708. else
  1709. lpicProp = icalproperty_new_x("TRUE");
  1710. icalproperty_set_x_name(lpicProp, "X-MOZ-SEND-INVITATIONS");
  1711. icalcomponent_add_property(lpicEvent, lpicProp);
  1712. // I am the Organizer
  1713. wstrBuf = L"mailto:" + (strRepsSenderEmailAddr.empty()? strSenderEmailAddr : strRepsSenderEmailAddr);
  1714. lpicProp = icalproperty_new_organizer(m_converter.convert_to<string>(m_strCharset.c_str(), wstrBuf, rawsize(wstrBuf), CHARSET_WCHAR).c_str());
  1715. wstrBuf = strRepsSenderName.empty()? strSenderName : strRepsSenderName;
  1716. if (!wstrBuf.empty())
  1717. icalproperty_add_parameter(lpicProp, icalparameter_new_cn(m_converter.convert_to<string>(m_strCharset.c_str(), wstrBuf, rawsize(wstrBuf), CHARSET_WCHAR).c_str()) );
  1718. wstrBuf = L"mailto:" + strSenderEmailAddr;
  1719. if (!strSenderEmailAddr.empty() && strSenderEmailAddr != strRepsSenderEmailAddr)
  1720. icalproperty_add_parameter(lpicProp, icalparameter_new_sentby(m_converter.convert_to<string>(m_strCharset.c_str(), wstrBuf, rawsize(wstrBuf), CHARSET_WCHAR).c_str()) );
  1721. icalcomponent_add_property(lpicEvent, lpicProp);
  1722. } else {
  1723. // normal calendar item
  1724. icMethod = ICAL_METHOD_PUBLISH;
  1725. }
  1726. }
  1727. *lpicMethod = icMethod;
  1728. return hrSuccess;
  1729. }
  1730. /**
  1731. * Sets default time properties in the ical event. The following ical
  1732. * properties are added:
  1733. * - CREATED
  1734. * - LAST-MODIFIED
  1735. * - DTSTAMP
  1736. *
  1737. * @param[in] lpMsgProps All (required) properties of the message to convert to ical
  1738. * @param[in] ulMsgProps Number of properties in lpMsgProps
  1739. * @param[in] lpicTZinfo ical timezone object to set times in, (unused in this version, all times set here are always UTC)
  1740. * @param[in] strTZid name of the given ical timezone, (unused in this version, all times set here are always UTC)
  1741. * @param[in,out] lpEvent The ical event to modify
  1742. *
  1743. * @return MAPI error code
  1744. */
  1745. HRESULT VConverter::HrSetTimeProperties(LPSPropValue lpMsgProps, ULONG ulMsgProps, icaltimezone *lpicTZinfo, const std::string &strTZid, icalcomponent *lpEvent)
  1746. {
  1747. HRESULT hr = hrSuccess;
  1748. icalproperty *lpProp = NULL;
  1749. icaltimetype ittICalTime;
  1750. bool bHasOwnerCriticalChange = false;
  1751. // Set creation time / CREATED
  1752. auto lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_CREATION_TIME);
  1753. if (lpPropVal) {
  1754. ittICalTime = icaltime_from_timet_with_zone(FileTimeToUnixTime(lpPropVal->Value.ft.dwHighDateTime, lpPropVal->Value.ft.dwLowDateTime), 0, nullptr);
  1755. ittICalTime.is_utc = 1;
  1756. lpProp = icalproperty_new_created(ittICalTime);
  1757. icalcomponent_add_property(lpEvent, lpProp);
  1758. }
  1759. // exchange 2003 is using DTSTAMP for 'X-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE'
  1760. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_OWNERCRITICALCHANGE], PT_SYSTIME));
  1761. if (lpPropVal) {
  1762. ittICalTime = icaltime_from_timet_with_zone(FileTimeToUnixTime(lpPropVal->Value.ft.dwHighDateTime, lpPropVal->Value.ft.dwLowDateTime), false, icaltimezone_get_utc_timezone());
  1763. lpProp = icalproperty_new_dtstamp(ittICalTime);
  1764. icalcomponent_add_property(lpEvent,lpProp);
  1765. bHasOwnerCriticalChange = true;
  1766. }
  1767. // Set modification time / LAST-MODIFIED + DTSTAMP
  1768. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_LAST_MODIFICATION_TIME);
  1769. if (lpPropVal) {
  1770. ittICalTime = icaltime_from_timet_with_zone(FileTimeToUnixTime(lpPropVal->Value.ft.dwHighDateTime, lpPropVal->Value.ft.dwLowDateTime), 0, nullptr);
  1771. ittICalTime.is_utc = 1;
  1772. lpProp = icalproperty_new_lastmodified(ittICalTime);
  1773. icalcomponent_add_property(lpEvent,lpProp);
  1774. if (!bHasOwnerCriticalChange) {
  1775. lpProp = icalproperty_new_dtstamp(ittICalTime);
  1776. icalcomponent_add_property(lpEvent,lpProp);
  1777. }
  1778. }
  1779. return hr;
  1780. }
  1781. /**
  1782. * Helper function for HrSetOrganizerAndAttendees to add recipients
  1783. * from lpMessage to the ical object.
  1784. *
  1785. * @param[in] lpMessage The message to process the RecipientsTable of
  1786. * @param[in] strOrganizer The email address of the organizer, which is excluded as attendee
  1787. * @param[in,out] lpicEvent The event to modify
  1788. *
  1789. * @return MAPI error code.
  1790. */
  1791. HRESULT VConverter::HrSetICalAttendees(LPMESSAGE lpMessage, const std::wstring &strOrganizer, icalcomponent *lpicEvent)
  1792. {
  1793. HRESULT hr = hrSuccess;
  1794. icalproperty *lpProp = NULL;
  1795. icalparameter *lpParam = NULL;
  1796. object_ptr<IMAPITable> lpTable;
  1797. rowset_ptr lpRows;
  1798. ULONG ulCount = 0;
  1799. wstring strName, strType, strEmailAddress;
  1800. static constexpr const SizedSPropTagArray(7, sptaRecipProps) =
  1801. {7, {PR_ENTRYID, PR_DISPLAY_NAME_W, PR_ADDRTYPE_A,
  1802. PR_EMAIL_ADDRESS_A, PR_RECIPIENT_FLAGS, PR_RECIPIENT_TYPE,
  1803. PR_RECIPIENT_TRACKSTATUS}};
  1804. hr = lpMessage->GetRecipientTable(0, &~lpTable);
  1805. if (hr != hrSuccess)
  1806. return hr;
  1807. hr = lpTable->SetColumns(sptaRecipProps, 0);
  1808. if (hr != hrSuccess)
  1809. return hr;
  1810. hr = lpTable->QueryRows(-1, 0, &~lpRows);
  1811. if (hr != hrSuccess)
  1812. return hr;
  1813. // Set all recipients into icalcomponent lpicEvent
  1814. for (ulCount = 0; ulCount < lpRows->cRows; ++ulCount) {
  1815. // ZARAFA types go correct because of addressbook, (slow?, should use PR_SMTP_ADDRESS?)
  1816. // SMTP types go correct because of PR_EMAIL_ADDRESS
  1817. hr = HrGetAddress(m_lpAdrBook, lpRows->aRow[ulCount].lpProps, lpRows->aRow[ulCount].cValues,
  1818. PR_ENTRYID, PR_DISPLAY_NAME_W, PR_ADDRTYPE_A, PR_EMAIL_ADDRESS_A,
  1819. strName, strType, strEmailAddress);
  1820. // skip the organiser if present in the recipient table.
  1821. if (hr != hrSuccess || strEmailAddress == strOrganizer)
  1822. continue;
  1823. // flags set to 3 is organizer, so skip that entry
  1824. auto lpPropVal = PCpropFindProp(lpRows->aRow[ulCount].lpProps, lpRows->aRow[ulCount].cValues, PR_RECIPIENT_FLAGS);
  1825. if (lpPropVal != NULL && lpPropVal->Value.ul == 3)
  1826. continue;
  1827. lpPropVal = PCpropFindProp(lpRows->aRow[ulCount].lpProps, lpRows->aRow[ulCount].cValues, PR_RECIPIENT_TYPE);
  1828. if (lpPropVal == NULL)
  1829. continue;
  1830. switch (lpPropVal->Value.ul) {
  1831. case MAPI_TO:
  1832. lpParam = icalparameter_new_role(ICAL_ROLE_REQPARTICIPANT);
  1833. break;
  1834. case MAPI_CC:
  1835. lpParam = icalparameter_new_role(ICAL_ROLE_OPTPARTICIPANT);
  1836. break;
  1837. case MAPI_BCC:
  1838. lpParam = icalparameter_new_role(ICAL_ROLE_NONPARTICIPANT);
  1839. break;
  1840. default:
  1841. continue;
  1842. }
  1843. strEmailAddress.insert(0, L"mailto:");
  1844. lpProp = icalproperty_new_attendee(m_converter.convert_to<string>(m_strCharset.c_str(), strEmailAddress, rawsize(strEmailAddress), CHARSET_WCHAR).c_str());
  1845. icalproperty_add_parameter(lpProp, lpParam);
  1846. lpPropVal = PCpropFindProp(lpRows->aRow[ulCount].lpProps, lpRows->aRow[ulCount].cValues, PR_RECIPIENT_TRACKSTATUS);
  1847. if (lpPropVal != NULL) {
  1848. if (lpPropVal->Value.ul == 2)
  1849. icalproperty_add_parameter(lpProp, icalparameter_new_partstat(ICAL_PARTSTAT_TENTATIVE));
  1850. else if (lpPropVal->Value.ul == 3)
  1851. icalproperty_add_parameter(lpProp, icalparameter_new_partstat(ICAL_PARTSTAT_ACCEPTED));
  1852. else if (lpPropVal->Value.ul == 4)
  1853. icalproperty_add_parameter(lpProp, icalparameter_new_partstat(ICAL_PARTSTAT_DECLINED));
  1854. else {
  1855. icalproperty_add_parameter(lpProp, icalparameter_new_partstat(ICAL_PARTSTAT_NEEDSACTION));
  1856. icalproperty_add_parameter(lpProp, icalparameter_new_rsvp(ICAL_RSVP_TRUE));
  1857. }
  1858. } else {
  1859. // make sure clients are requested to send a reply on meeting requests
  1860. icalproperty_add_parameter(lpProp, icalparameter_new_partstat(ICAL_PARTSTAT_NEEDSACTION));
  1861. icalproperty_add_parameter(lpProp, icalparameter_new_rsvp(ICAL_RSVP_TRUE));
  1862. }
  1863. if (!strName.empty())
  1864. icalproperty_add_parameter(lpProp, icalparameter_new_cn(m_converter.convert_to<string>(m_strCharset.c_str(), strName, rawsize(strName), CHARSET_WCHAR).c_str()));
  1865. icalcomponent_add_property(lpicEvent, lpProp);
  1866. }
  1867. return hr;
  1868. }
  1869. /**
  1870. * Sets the busy status in the ical event. The following properties
  1871. * are added:
  1872. * - TRANSP
  1873. * - X-MICROSOFT-CDO-INTENDEDSTATUS
  1874. *
  1875. * @param[in] lpMessage The MAPI message to get the busy status from for the X property
  1876. * @param[in] ulBusyStatus The normal busy status to set in the TRANSP property
  1877. * @param[in,out] lpicEvent The ical event to modify
  1878. *
  1879. * @return Always return hrSuccess
  1880. */
  1881. HRESULT VConverter::HrSetBusyStatus(LPMESSAGE lpMessage, ULONG ulBusyStatus, icalcomponent *lpicEvent)
  1882. {
  1883. HRESULT hr = hrSuccess;
  1884. memory_ptr<SPropValue> lpSpropVal;
  1885. icalproperty *lpicProp = NULL;
  1886. // set the TRANSP property
  1887. if (ulBusyStatus == 0)
  1888. lpicProp = icalproperty_new_transp(ICAL_TRANSP_TRANSPARENT);
  1889. else
  1890. lpicProp = icalproperty_new_transp(ICAL_TRANSP_OPAQUE);
  1891. icalcomponent_add_property(lpicEvent, lpicProp);
  1892. // set the X-MICROSOFT-CDO-INTENDEDSTATUS property
  1893. hr = HrGetOneProp(lpMessage, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_INTENDEDBUSYSTATUS], PT_LONG), &~lpSpropVal);
  1894. if(hr == hrSuccess && lpSpropVal->Value.ul != (ULONG)-1)
  1895. ulBusyStatus = lpSpropVal->Value.ul;
  1896. switch (ulBusyStatus) {
  1897. case 0:
  1898. lpicProp = icalproperty_new_x("FREE");
  1899. break;
  1900. case 1:
  1901. lpicProp = icalproperty_new_x("TENTATIVE");
  1902. break;
  1903. default:
  1904. case 2:
  1905. lpicProp = icalproperty_new_x("BUSY");
  1906. break;
  1907. case 3:
  1908. lpicProp = icalproperty_new_x("OOF");
  1909. break;
  1910. }
  1911. icalproperty_set_x_name(lpicProp, "X-MICROSOFT-CDO-INTENDEDSTATUS");
  1912. icalcomponent_add_property(lpicEvent, lpicProp);
  1913. return hrSuccess;
  1914. }
  1915. /**
  1916. * Add extra Microsoft and Mozilla X headers. These are required for
  1917. * client interchange and compatibility. The following ical properties
  1918. * are added:
  1919. * - X-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE
  1920. * - X-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE
  1921. * - X-MICROSOFT-CDO-APPT-SEQUENCE
  1922. * - X-MICROSOFT-CDO-OWNERAPPTID
  1923. * - X-MOZ-GENERATION
  1924. * - X-MICROSOFT-CDO-ALLDAYEVENT
  1925. *
  1926. * @param[in] ulMsgProps Number of properties in lpMsgProps
  1927. * @param[in] lpMsgProps Properties used for the conversion
  1928. * @param[in] lpMessage The message to convert the PR_RTF_COMPRESSED from
  1929. * @param[in,out] lpEvent ical item to be modified
  1930. *
  1931. * @return Always return hrSuccess
  1932. */
  1933. HRESULT VConverter::HrSetXHeaders(ULONG ulMsgProps, LPSPropValue lpMsgProps, LPMESSAGE lpMessage, icalcomponent *lpEvent)
  1934. {
  1935. icaltimetype icCriticalChange;
  1936. icalvalue *lpicValue = NULL;
  1937. icalproperty *lpProp = NULL;
  1938. time_t ttCriticalChange = 0;
  1939. ULONG ulApptSeqNo = 0;
  1940. ULONG ulOwnerApptID = 0;
  1941. char *lpszTemp = NULL;
  1942. bool blIsAllday = false;
  1943. // set X-MICROSOFT-CDO & X-MOZ properties
  1944. // X-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE
  1945. auto lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_OWNERCRITICALCHANGE], PT_SYSTIME));
  1946. if (lpPropVal != nullptr)
  1947. FileTimeToUnixTime(lpPropVal->Value.ft, &ttCriticalChange);
  1948. else
  1949. ttCriticalChange = time(NULL);
  1950. icCriticalChange = icaltime_from_timet_with_zone(ttCriticalChange, false, icaltimezone_get_utc_timezone());
  1951. lpicValue = icalvalue_new_datetime(icCriticalChange);
  1952. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  1953. lpProp = icalproperty_new_x(lpszTemp);
  1954. icalmemory_free_buffer(lpszTemp);
  1955. icalproperty_set_x_name(lpProp, "X-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE");
  1956. icalcomponent_add_property(lpEvent, lpProp);
  1957. icalvalue_free(lpicValue);
  1958. // X-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE
  1959. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_ATTENDEECRITICALCHANGE], PT_SYSTIME));
  1960. if (lpPropVal != nullptr)
  1961. FileTimeToUnixTime(lpPropVal->Value.ft, &ttCriticalChange);
  1962. else
  1963. ttCriticalChange = time(NULL);
  1964. icCriticalChange = icaltime_from_timet_with_zone(ttCriticalChange, false, icaltimezone_get_utc_timezone());
  1965. lpicValue = icalvalue_new_datetime(icCriticalChange);
  1966. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  1967. lpProp = icalproperty_new_x(lpszTemp);
  1968. icalmemory_free_buffer(lpszTemp);
  1969. icalproperty_set_x_name(lpProp, "X-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE");
  1970. icalcomponent_add_property(lpEvent, lpProp);
  1971. icalvalue_free(lpicValue);
  1972. // X-MICROSOFT-CDO-APPT-SEQUENCE
  1973. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_APPTSEQNR], PT_LONG));
  1974. if (lpPropVal != nullptr)
  1975. ulApptSeqNo = lpPropVal->Value.ul;
  1976. else
  1977. ulApptSeqNo = 0;
  1978. lpicValue = icalvalue_new_integer(ulApptSeqNo);
  1979. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  1980. lpProp = icalproperty_new_x(lpszTemp);
  1981. icalmemory_free_buffer(lpszTemp);
  1982. icalproperty_set_x_name(lpProp, "X-MICROSOFT-CDO-APPT-SEQUENCE");
  1983. icalcomponent_add_property(lpEvent, lpProp);
  1984. icalvalue_free(lpicValue);
  1985. // X-MICROSOFT-CDO-OWNERAPPTID
  1986. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_OWNER_APPT_ID);
  1987. if (lpPropVal != nullptr)
  1988. ulOwnerApptID = lpPropVal->Value.ul;
  1989. else
  1990. ulOwnerApptID = -1;
  1991. lpicValue = icalvalue_new_integer(ulOwnerApptID);
  1992. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  1993. lpProp = icalproperty_new_x(lpszTemp);
  1994. icalmemory_free_buffer(lpszTemp);
  1995. icalproperty_set_x_name(lpProp, "X-MICROSOFT-CDO-OWNERAPPTID");
  1996. icalcomponent_add_property(lpEvent, lpProp);
  1997. icalvalue_free(lpicValue);
  1998. // X-MOZ-GENERATION
  1999. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZGEN], PT_LONG));
  2000. if (lpPropVal)
  2001. {
  2002. LONG ulXmozGen = 0;
  2003. icalvalue *lpicValue = NULL;
  2004. ulXmozGen = lpPropVal->Value.ul;
  2005. lpicValue = icalvalue_new_integer(ulXmozGen);
  2006. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  2007. lpProp = icalproperty_new_x(lpszTemp);
  2008. icalmemory_free_buffer(lpszTemp);
  2009. icalproperty_set_x_name(lpProp, "X-MOZ-GENERATION");
  2010. icalcomponent_add_property(lpEvent, lpProp);
  2011. icalvalue_free(lpicValue);
  2012. }
  2013. // X-MICROSOFT-CDO-ALLDAYEVENT
  2014. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_ALLDAYEVENT], PT_BOOLEAN));
  2015. if (lpPropVal != nullptr)
  2016. blIsAllday = (lpPropVal->Value.b == TRUE);
  2017. lpicValue = icalvalue_new_x(blIsAllday ? "TRUE" : "FALSE");
  2018. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  2019. lpProp = icalproperty_new_x(lpszTemp);
  2020. icalmemory_free_buffer(lpszTemp);
  2021. icalproperty_set_x_name(lpProp, "X-MICROSOFT-CDO-ALLDAYEVENT");
  2022. icalcomponent_add_property(lpEvent, lpProp);
  2023. icalvalue_free(lpicValue);
  2024. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_RTF_COMPRESSED);
  2025. if (lpPropVal == nullptr || Util::GetBestBody(lpMsgProps, ulMsgProps, fMapiUnicode) != PR_RTF_COMPRESSED)
  2026. return hrSuccess;
  2027. string rtf;
  2028. object_ptr<IStream> lpStream;
  2029. if (lpMessage->OpenProperty(PR_RTF_COMPRESSED, &IID_IStream, 0, MAPI_DEFERRED_ERRORS, &~lpStream) != hrSuccess)
  2030. return hrSuccess;
  2031. if (Util::HrStreamToString(lpStream, rtf) != hrSuccess)
  2032. return hrSuccess;
  2033. string rtfbase64;
  2034. rtfbase64 = base64_encode((unsigned char*)rtf.c_str(), rtf.size());
  2035. lpicValue = icalvalue_new_x(rtfbase64.c_str());
  2036. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  2037. lpProp = icalproperty_new_x(lpszTemp);
  2038. icalmemory_free_buffer(lpszTemp);
  2039. icalproperty_set_x_name(lpProp, "X-MICROSOFT-RTF");
  2040. icalcomponent_add_property(lpEvent, lpProp);
  2041. icalvalue_free(lpicValue);
  2042. return hrSuccess;
  2043. }
  2044. /**
  2045. * Possebly adds a VAlarm (reminder) in the given event.
  2046. *
  2047. * @note it is not described in the RFC how clients keep track (and
  2048. * thus disable) alarms, so we might be adding a VAlarm for an item
  2049. * where the client already disabled it.
  2050. * @param[in] ulProps The number of properties in lpProps
  2051. * @param[in] lpProps Properties of the message containing the reminder properties
  2052. * @param[in,out] lpicEvent The ical event to modify
  2053. *
  2054. * @return MAPI error code
  2055. */
  2056. HRESULT VConverter::HrSetVAlarm(ULONG ulProps, LPSPropValue lpProps, icalcomponent *lpicEvent)
  2057. {
  2058. HRESULT hr = hrSuccess;
  2059. icalcomponent *lpAlarm = NULL;
  2060. icalproperty *lpicProp = NULL;
  2061. time_t ttSnooze = 0;
  2062. time_t ttSnoozeSuffix = 0;
  2063. bool blxmozgen = false;
  2064. bool blisItemReccr = false;
  2065. char *lpszTemp = NULL;
  2066. LONG lRemindBefore = 0;
  2067. time_t ttReminderTime = 0;
  2068. bool bTask = false;
  2069. // find bool, skip if error or false
  2070. auto lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERSET], PT_BOOLEAN));
  2071. if (!lpPropVal || lpPropVal->Value.b == FALSE)
  2072. return hrSuccess;
  2073. lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERMINUTESBEFORESTART], PT_LONG));
  2074. if (lpPropVal)
  2075. lRemindBefore = lpPropVal->Value.l;
  2076. lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERTIME], PT_SYSTIME));
  2077. if (lpPropVal)
  2078. FileTimeToUnixTime(lpPropVal->Value.ft, &ttReminderTime);
  2079. lpPropVal = PCpropFindProp(lpProps, ulProps, PR_MESSAGE_CLASS);
  2080. if (lpPropVal && _tcsicmp(lpPropVal->Value.LPSZ, _T("IPM.Task")) == 0)
  2081. bTask = true;
  2082. hr = HrParseReminder(lRemindBefore, ttReminderTime, bTask, &lpAlarm);
  2083. if (hr != hrSuccess)
  2084. return hr;
  2085. icalcomponent_add_component(lpicEvent, lpAlarm);
  2086. lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRING], PT_BOOLEAN));
  2087. if(lpPropVal && lpPropVal->Value.b == TRUE)
  2088. blisItemReccr = true;
  2089. // retrieve the suffix time for property X-MOZ-SNOOZE-TIME
  2090. lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZ_SNOOZE_SUFFIX], PT_SYSTIME));
  2091. if(lpPropVal)
  2092. FileTimeToUnixTime(lpPropVal->Value.ft, &ttSnoozeSuffix);
  2093. // check latest snooze time
  2094. lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERNEXTTIME], PT_SYSTIME));
  2095. if (lpPropVal) {
  2096. icaltimetype icSnooze;
  2097. icalvalue *lpicValue = NULL;
  2098. std::string strSnoozeTime = "X-MOZ-SNOOZE-TIME";
  2099. // set timestamp in name for recurring items i.e. X-MOZ-SNOOZE-TIME-1231250400000000:20090107T132846Z
  2100. if (ttSnoozeSuffix != 0 && blisItemReccr)
  2101. strSnoozeTime += "-" + stringify(ttSnoozeSuffix) + "000000";
  2102. FileTimeToUnixTime(lpPropVal->Value.ft, &ttSnooze);
  2103. icSnooze = icaltime_from_timet_with_zone(ttSnooze, false, icaltimezone_get_utc_timezone());
  2104. lpicValue = icalvalue_new_datetime(icSnooze);
  2105. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  2106. lpicProp = icalproperty_new_x(lpszTemp);
  2107. icalmemory_free_buffer(lpszTemp);
  2108. icalproperty_set_x_name(lpicProp, strSnoozeTime.c_str());
  2109. icalcomponent_add_property(lpicEvent, lpicProp);
  2110. icalvalue_free(lpicValue);
  2111. }
  2112. lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZGEN], PT_LONG));
  2113. if (lpPropVal)
  2114. blxmozgen = true;
  2115. // send X-MOZ-LASTACK
  2116. lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_MOZLASTACK], PT_SYSTIME));
  2117. if (lpPropVal)
  2118. {
  2119. time_t ttLastAckTime = 0;
  2120. icaltimetype icModTime;
  2121. icalvalue *lpicValue = NULL;
  2122. FileTimeToUnixTime(lpPropVal->Value.ft, &ttLastAckTime);
  2123. //do not send X-MOZ-LASTACK if reminder older than last ack time
  2124. if(ttLastAckTime > ttSnooze && !blxmozgen)
  2125. return hrSuccess;
  2126. icModTime = icaltime_from_timet_with_zone(ttLastAckTime, false, icaltimezone_get_utc_timezone());
  2127. lpicValue = icalvalue_new_datetime(icModTime);
  2128. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  2129. lpicProp = icalproperty_new_x(lpszTemp);
  2130. icalmemory_free_buffer(lpszTemp);
  2131. icalproperty_set_x_name(lpicProp, "X-MOZ-LASTACK");
  2132. icalcomponent_add_property(lpicEvent, lpicProp);
  2133. icalvalue_free(lpicValue);
  2134. }
  2135. return hrSuccess;
  2136. }
  2137. /**
  2138. * Converts the plain text body of the MAPI message to an ical
  2139. * property (DESCRIPTION).
  2140. *
  2141. * We add some extra fixes on the body, so all ical clients can parse
  2142. * the body correctly.
  2143. *
  2144. * @param[in] lpMessage The message to convert the PR_BODY from
  2145. * @param[out] lppicProp The ical property containing the description, when hrSuccess is returned.
  2146. *
  2147. * @return MAPI error code
  2148. */
  2149. HRESULT VConverter::HrSetBody(LPMESSAGE lpMessage, icalproperty **lppicProp)
  2150. {
  2151. HRESULT hr = hrSuccess;
  2152. object_ptr<IStream> lpStream;
  2153. STATSTG sStreamStat;
  2154. std::wstring strBody;
  2155. std::unique_ptr<wchar_t[]> lpBody;
  2156. hr = lpMessage->OpenProperty(PR_BODY_W, &IID_IStream, 0, MAPI_DEFERRED_ERRORS, &~lpStream);
  2157. if (hr != hrSuccess)
  2158. return hr;
  2159. hr = lpStream->Stat(&sStreamStat, 0);
  2160. if (hr != hrSuccess)
  2161. return hr;
  2162. if (sStreamStat.cbSize.LowPart == 0)
  2163. return MAPI_E_NOT_FOUND;
  2164. lpBody.reset(new WCHAR[sStreamStat.cbSize.LowPart + sizeof(WCHAR)]);
  2165. memset(lpBody.get(), 0, (sStreamStat.cbSize.LowPart+1) * sizeof(WCHAR));
  2166. hr = lpStream->Read(lpBody.get(), sStreamStat.cbSize.LowPart * sizeof(WCHAR), NULL);
  2167. if (hr != hrSuccess)
  2168. return hr;
  2169. // The body is converted as OL2003 does not parse '\r' & '\t' correctly
  2170. // Newer versions also have some issues parsing these chars
  2171. // RFC specifies that new lines should be CRLF
  2172. StringTabtoSpaces(lpBody.get(), &strBody);
  2173. StringCRLFtoLF(strBody, &strBody);
  2174. *lppicProp = icalproperty_new_description(m_converter.convert_to<string>(m_strCharset.c_str(), strBody, rawsize(strBody), CHARSET_WCHAR).c_str());
  2175. return hrSuccess;
  2176. }
  2177. /**
  2178. * No specific properties on the base level. Override this function if
  2179. * required (currently VTODO only).
  2180. *
  2181. * @param[in] ulProps Number of properties in lpProps
  2182. * @param[in] lpProps Properties of the message to convert
  2183. * @param[in,out] lpicEvent The ical object to modify
  2184. *
  2185. * @return Always return hrSuccess
  2186. */
  2187. HRESULT VConverter::HrSetItemSpecifics(ULONG ulProps, LPSPropValue lpProps, icalcomponent *lpicEvent)
  2188. {
  2189. return hrSuccess;
  2190. }
  2191. /**
  2192. * Adds the recurrence-id property to the event if we're handling an exception.
  2193. *
  2194. * @param[in] lpMsgProps Contains all the (required) lpMessage properties for conversion
  2195. * @param[in] ulMsgProps Number of properties in lpMsgProps
  2196. * @param[in] lpicTZinfo Ical Timezone to set all time related properties in
  2197. * @param[in] strTZid Name of the timezone
  2198. * @param[in,out] lpEvent The ical event to modify
  2199. *
  2200. * @return MAPI error code
  2201. */
  2202. HRESULT VConverter::HrSetRecurrenceID(LPSPropValue lpMsgProps, ULONG ulMsgProps, icaltimezone *lpicTZinfo, const std::string &strTZid, icalcomponent *lpEvent)
  2203. {
  2204. bool bIsSeriesAllDay = false;
  2205. icaltimetype icTime = {0};
  2206. std::string strUid;
  2207. time_t tRecId = 0;
  2208. ULONG ulRecurStartTime = -1; // as 0 states start of day
  2209. ULONG ulRecurEndTime = -1; // as 0 states start of day
  2210. // We cannot check if PROP_ISEXCEPTION is set to TRUE, since Outlook sends accept messages on excetions with that property set to false.
  2211. auto lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_ISEXCEPTION], PT_BOOLEAN));
  2212. if (!lpPropVal || lpPropVal->Value.b == FALSE) {
  2213. auto lpPropGlobal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_GOID], PT_BINARY));
  2214. if (!lpPropGlobal)
  2215. return hrSuccess;
  2216. auto lpPropClean = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_CLEANID], PT_BINARY));
  2217. if (!lpPropClean)
  2218. return hrSuccess;
  2219. if (lpPropClean->Value.bin.cb != lpPropGlobal->Value.bin.cb)
  2220. return hrSuccess;
  2221. if (memcmp(lpPropClean->Value.bin.lpb, lpPropGlobal->Value.bin.lpb, lpPropGlobal->Value.bin.cb) == 0)
  2222. return hrSuccess;
  2223. // different timestamp in dispidGlobalObjectID, export RECURRENCE-ID
  2224. }
  2225. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURSTARTTIME], PT_LONG));
  2226. if (lpPropVal)
  2227. ulRecurStartTime = lpPropVal->Value.ul;
  2228. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURENDTIME], PT_LONG));
  2229. if (lpPropVal)
  2230. ulRecurEndTime = lpPropVal->Value.ul;
  2231. // check to know if series is all day or not.
  2232. // ulRecurStartTime = 0 -> recurrence series starts at 00:00 AM
  2233. // ulRecurEndTime = 24 * 4096 -> recurrence series ends at 12:00 PM
  2234. // 60 sec -> highest pow of 2 after 60 -> 64
  2235. // 60 mins -> 60 * 64 = 3840 -> highest pow of 2 after 3840 -> 4096
  2236. if (ulRecurStartTime == 0 && ulRecurEndTime == (24 * 4096))
  2237. bIsSeriesAllDay = true;
  2238. // set Recurrence-ID for exception msg if dispidRecurringbase prop is present
  2239. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRINGBASE], PT_SYSTIME));
  2240. if (!lpPropVal) {
  2241. // This code happens when we're sending acceptance mail for an exception.
  2242. // if RecurringBase prop is not present then retrieve date from GlobalObjId from 16-19th bytes
  2243. // combine this date with time from dispidStartRecurrenceTime and set it as RECURRENCE-ID
  2244. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_GOID], PT_BINARY));
  2245. if (!lpPropVal)
  2246. return hrSuccess;
  2247. // @todo don't do this calculation using a std::string
  2248. strUid = bin2hex(lpPropVal->Value.bin.cb, lpPropVal->Value.bin.lpb);
  2249. if(!IsOutlookUid(strUid))
  2250. return hrSuccess;
  2251. if(strUid.substr(32, 8).compare("00000000") == 0 && ulRecurStartTime == (ULONG)-1)
  2252. return hrSuccess;
  2253. icTime.year = strtol(strUid.substr(32, 4).c_str(), NULL, 16);
  2254. icTime.month = strtol(strUid.substr(36, 2).c_str(), NULL, 16);
  2255. icTime.day = strtol(strUid.substr(38, 2).c_str(), NULL, 16);
  2256. // Although the timestamp is probably 0, it does not seem to matter.
  2257. icTime.hour = ulRecurStartTime / 4096;
  2258. ulRecurStartTime -= icTime.hour * 4096;
  2259. icTime.minute = ulRecurStartTime / 64;
  2260. icTime.second = ulRecurStartTime - icTime.minute * 64;
  2261. // set correct date for all_day
  2262. if(bIsSeriesAllDay)
  2263. tRecId = icaltime_as_timet(icTime);
  2264. else
  2265. tRecId = icaltime_as_timet_with_zone(icTime,lpicTZinfo);
  2266. } else {
  2267. tRecId = FileTimeToUnixTime(lpPropVal->Value.ft.dwHighDateTime, lpPropVal->Value.ft.dwLowDateTime);
  2268. }
  2269. return HrSetTimeProperty(tRecId, bIsSeriesAllDay, lpicTZinfo, strTZid,
  2270. ICAL_RECURRENCEID_PROPERTY, lpEvent);
  2271. }
  2272. /**
  2273. * Sets the RRULE and add exceptions to ical data from the mapi message.
  2274. *
  2275. * @param[in] lpMessage The source mapi message to be converted to ical data.
  2276. * @param[in,out] lpicEvent The icalcomponent to which RRULE is added.
  2277. * @param[in] lpicTZinfo The icaltimezone pointer
  2278. * @param[in] strTZid The timezone string ID.
  2279. * @param[out] lpEventList The list of icalcomponent containing exceptions.
  2280. *
  2281. * @return MAPI error code
  2282. *
  2283. * @retval MAPI_E_NOT_FOUND Recurrencestate blob has errors
  2284. * @retval MAPI_E_INVALID_PARAMETER One of the parameters for creating RRULE from recurrence blob is invalid
  2285. */
  2286. HRESULT VConverter::HrSetRecurrence(LPMESSAGE lpMessage, icalcomponent *lpicEvent, icaltimezone *lpicTZinfo, const std::string &strTZid, std::list<icalcomponent*> *lpEventList)
  2287. {
  2288. HRESULT hr = hrSuccess;
  2289. ULONG ulRecurrenceStateTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRENCESTATE], PT_BINARY);
  2290. bool bIsAllDay = false;
  2291. bool bIsAllDayException = false;
  2292. memory_ptr<SPropValue> lpSpropArray;
  2293. LPSPropValue lpSPropRecVal = NULL;
  2294. recurrence cRecurrence;
  2295. object_ptr<IStream> lpStream;
  2296. STATSTG sStreamStat;
  2297. ICalRecurrence cICalRecurrence;
  2298. icalcomponent *lpicComp = NULL;
  2299. icalproperty *lpicProp = NULL;
  2300. ULONG ulModCount = 0;
  2301. ULONG ulModifications = 0;
  2302. ULONG cbsize = 0;
  2303. ULONG ulFlag = 0;
  2304. time_t tNewTime = 0;
  2305. time_t tExceptionStart = 0;
  2306. std::list<icalcomponent*> lstExceptions;
  2307. TIMEZONE_STRUCT zone;
  2308. SizedSPropTagArray(6, proptags) =
  2309. {6, {PR_MESSAGE_CLASS_A,
  2310. CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRENCEPATTERN], PT_UNICODE),
  2311. CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRENCESTATE], PT_BINARY),
  2312. CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_ALLDAYEVENT], PT_BOOLEAN),
  2313. CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_STATUS], PT_LONG),
  2314. CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_RECURRSTATE], PT_BINARY)}};
  2315. hr = lpMessage->GetProps(proptags, 0, &cbsize, &~lpSpropArray);
  2316. if (FAILED(hr))
  2317. return hr;
  2318. if ((PROP_TYPE(lpSpropArray[0].ulPropTag) != PT_ERROR)
  2319. && (strcasecmp(lpSpropArray[0].Value.lpszA, "IPM.Task") == 0)) {
  2320. ulRecurrenceStateTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_RECURRSTATE], PT_BINARY);
  2321. lpSPropRecVal = &lpSpropArray[5];
  2322. ulFlag = RECURRENCE_STATE_TASKS;
  2323. } else {
  2324. ulRecurrenceStateTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRENCESTATE], PT_BINARY);
  2325. lpSPropRecVal = &lpSpropArray[2];
  2326. ulFlag = RECURRENCE_STATE_CALENDAR;
  2327. }
  2328. // there are no completed recurring task in OL, so return
  2329. if ((PROP_TYPE(lpSpropArray[4].ulPropTag) != PT_ERROR) && lpSpropArray[4].Value.ul == 2)
  2330. return hr;
  2331. if (PROP_TYPE(lpSpropArray[1].ulPropTag) != PT_ERROR)
  2332. {
  2333. lpicProp = icalproperty_new_x(m_converter.convert_to<string>(m_strCharset.c_str(), lpSpropArray[1].Value.lpszW, rawsize(lpSpropArray[1].Value.lpszW), CHARSET_WCHAR).c_str());
  2334. icalproperty_set_x_name(lpicProp, "X-KOPANO-REC-PATTERN");
  2335. icalcomponent_add_property(lpicEvent, lpicProp);
  2336. }
  2337. if ((PROP_TYPE(lpSPropRecVal->ulPropTag) != PT_ERROR)) {
  2338. hr = cRecurrence.HrLoadRecurrenceState(reinterpret_cast<const char *>(lpSPropRecVal->Value.bin.lpb), lpSPropRecVal->Value.bin.cb, ulFlag);
  2339. } else if (lpSPropRecVal->Value.err == MAPI_E_NOT_ENOUGH_MEMORY) {
  2340. // open property and read full blob
  2341. hr = lpMessage->OpenProperty(ulRecurrenceStateTag, &IID_IStream, 0, MAPI_DEFERRED_ERRORS, &~lpStream);
  2342. if (hr != hrSuccess)
  2343. return hr;
  2344. hr = lpStream->Stat(&sStreamStat, 0);
  2345. if (hr != hrSuccess)
  2346. return hr;
  2347. std::unique_ptr<char[]> lpRecurrenceData(new char[sStreamStat.cbSize.LowPart]);
  2348. hr = lpStream->Read(lpRecurrenceData.get(), sStreamStat.cbSize.LowPart, NULL);
  2349. if (hr != hrSuccess)
  2350. return hr;
  2351. hr = cRecurrence.HrLoadRecurrenceState(lpRecurrenceData.get(), sStreamStat.cbSize.LowPart, ulFlag);
  2352. } else {
  2353. // When exception is created in MR, the IsRecurring is set - true by OL
  2354. // but Recurring state is not set in MR.
  2355. return hrSuccess;
  2356. }
  2357. if (FAILED(hr))
  2358. return hr;
  2359. if (PROP_TYPE(lpSpropArray[3].ulPropTag) != PT_ERROR)
  2360. bIsAllDay = (lpSpropArray[3].Value.b == TRUE);
  2361. if (m_iCurrentTimeZone == m_mapTimeZones->end()) {
  2362. hr = HrGetTzStruct("Etc/UTC", &zone);
  2363. if (hr != hrSuccess)
  2364. return hr;
  2365. } else {
  2366. zone = m_iCurrentTimeZone->second;
  2367. }
  2368. // now that we have the recurrence state class, we can create rrules in lpicEvent
  2369. hr = cICalRecurrence.HrCreateICalRecurrence(zone, bIsAllDay, &cRecurrence, lpicEvent);
  2370. if (hr != hrSuccess)
  2371. return hr;
  2372. // all modifications create new event item:
  2373. // RECURRENCE-ID: contains local timezone timestamp of item that is changed
  2374. // other: CREATED, LAST-MODIFIED, DTSTAMP, UID (copy from original)
  2375. // and then exception properties are replaced
  2376. ulModCount = cRecurrence.getModifiedCount();
  2377. for (ULONG i = 0; i < ulModCount; ++i) {
  2378. SPropValuePtr lpMsgProps;
  2379. ULONG ulMsgProps = 0;
  2380. const SPropValue *lpProp = NULL;
  2381. icalproperty_method icMethod = ICAL_METHOD_NONE;
  2382. icalcomp_ptr lpicException;
  2383. ulModifications = cRecurrence.getModifiedFlags(i);
  2384. bIsAllDayException = bIsAllDay;
  2385. hr = cICalRecurrence.HrMakeICalException(lpicEvent, &unique_tie(lpicException));
  2386. if (hr != hrSuccess)
  2387. continue;
  2388. tExceptionStart = tNewTime = cRecurrence.getModifiedStartDateTime(i);
  2389. object_ptr<IMessage> lpException;
  2390. hr = HrGetExceptionMessage(lpMessage, tExceptionStart, &~lpException);
  2391. if (hr != hrSuccess)
  2392. {
  2393. hr = hrSuccess;
  2394. continue;
  2395. }
  2396. hr = lpException->GetProps(NULL, MAPI_UNICODE, &ulMsgProps, &~lpMsgProps);
  2397. if (FAILED(hr))
  2398. continue;
  2399. hr = HrSetOrganizerAndAttendees(lpMessage, lpException,
  2400. ulMsgProps, lpMsgProps, &icMethod, lpicException.get());
  2401. if (hr != hrSuccess)
  2402. continue;
  2403. if (ulModifications & ARO_SUBTYPE)
  2404. {
  2405. icalvalue *lpicValue = NULL;
  2406. char *lpszTemp = NULL;
  2407. lpProp = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_ALLDAYEVENT], PT_BOOLEAN));
  2408. if (lpProp)
  2409. bIsAllDayException = (lpProp->Value.b == TRUE);
  2410. lpicProp = icalcomponent_get_first_property(lpicException.get(), ICAL_X_PROPERTY);
  2411. while (lpicProp && (strcmp(icalproperty_get_x_name(lpicProp), "X-MICROSOFT-CDO-ALLDAYEVENT") != 0))
  2412. lpicProp = icalcomponent_get_next_property(lpicException.get(), ICAL_X_PROPERTY);
  2413. if (lpicProp) {
  2414. icalcomponent_remove_property(lpicException.get(), lpicProp);
  2415. icalproperty_free(lpicProp);
  2416. }
  2417. lpicValue = icalvalue_new_x(bIsAllDayException ? "TRUE" : "FALSE");
  2418. lpszTemp = icalvalue_as_ical_string_r(lpicValue);
  2419. lpicProp = icalproperty_new_x(lpszTemp);
  2420. icalmemory_free_buffer(lpszTemp);
  2421. icalproperty_set_x_name(lpicProp, "X-MICROSOFT-CDO-ALLDAYEVENT");
  2422. icalcomponent_add_property(lpicException.get(), lpicProp);
  2423. icalvalue_free(lpicValue);
  2424. }
  2425. // 1. get new StartDateTime and EndDateTime from exception and make DTSTART and DTEND in sTimeZone
  2426. tNewTime = LocalToUTC(tNewTime, m_iCurrentTimeZone->second);
  2427. hr = HrSetTimeProperty(tNewTime, bIsAllDayException, lpicTZinfo,
  2428. strTZid, ICAL_DTSTART_PROPERTY, lpicException.get());
  2429. if (hr != hrSuccess)
  2430. continue;
  2431. tNewTime = LocalToUTC(cRecurrence.getModifiedEndDateTime(i), m_iCurrentTimeZone->second);
  2432. hr = HrSetTimeProperty(tNewTime, bIsAllDayException, lpicTZinfo,
  2433. strTZid, ICAL_DTEND_PROPERTY, lpicException.get());
  2434. if (hr != hrSuccess)
  2435. continue;
  2436. lpProp = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRINGBASE], PT_SYSTIME));
  2437. if (!lpProp)
  2438. lpProp = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_OLDSTART], PT_SYSTIME));
  2439. if (lpProp) {
  2440. tNewTime = FileTimeToUnixTime(lpProp->Value.ft.dwHighDateTime, lpProp->Value.ft.dwLowDateTime);
  2441. hr = HrSetTimeProperty(tNewTime, bIsAllDay, lpicTZinfo,
  2442. strTZid, ICAL_RECURRENCEID_PROPERTY, lpicException.get());
  2443. if (hr != hrSuccess)
  2444. continue;
  2445. }
  2446. // 2. for each (useful?) bit in ulOverrideFlags, set property
  2447. if (ulModifications & ARO_SUBJECT) {
  2448. // find the previous value, and remove it
  2449. lpicProp = icalcomponent_get_first_property(lpicException.get(), ICAL_SUMMARY_PROPERTY);
  2450. if (lpicProp) {
  2451. icalcomponent_remove_property(lpicException.get(), lpicProp);
  2452. icalproperty_free(lpicProp);
  2453. }
  2454. const wstring wstrTmp = cRecurrence.getModifiedSubject(i);
  2455. icalcomponent_add_property(lpicException.get(), icalproperty_new_summary(m_converter.convert_to<string>(m_strCharset.c_str(), wstrTmp, rawsize(wstrTmp), CHARSET_WCHAR).c_str()));
  2456. }
  2457. if (ulModifications & ARO_MEETINGTYPE) {
  2458. // make this in invite, cancel, ... ?
  2459. }
  2460. if (ulModifications & ARO_REMINDERDELTA && !(ulModifications & ARO_REMINDERSET))
  2461. HrUpdateReminderTime(lpicException.get(), cRecurrence.getModifiedReminderDelta(i));
  2462. if (ulModifications & ARO_REMINDERSET) {
  2463. // Outlook is nasty!
  2464. // If you make an exception reminder enable with the DEFAULT 15 minutes alarm,
  2465. // the value is NOT saved in the exception attachment.
  2466. // That's why I don't open the attachment if the value isn't going to be present anyway.
  2467. // Also, it (the default) is present in the main item ... not very logical.
  2468. LONG lRemindBefore = 0;
  2469. time_t ttReminderTime = 0;
  2470. if (ulModifications & ARO_REMINDERDELTA) {
  2471. lpProp = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERMINUTESBEFORESTART], PT_LONG));
  2472. lRemindBefore = lpProp ? lpProp->Value.l : 15;
  2473. lpProp = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_REMINDERTIME], PT_LONG));
  2474. if (lpProp)
  2475. FileTimeToUnixTime(lpProp->Value.ft, &ttReminderTime);
  2476. }
  2477. // add new valarm
  2478. // although a previous valarm should not be here, the webaccess always says it's been changed, so we remove the old one too
  2479. lpicComp = icalcomponent_get_first_component(lpicException.get(), ICAL_VALARM_COMPONENT);
  2480. if (lpicComp) {
  2481. icalcomponent_remove_component(lpicException.get(), lpicComp);
  2482. icalcomponent_free(lpicComp);
  2483. }
  2484. lpicComp = NULL;
  2485. if (cRecurrence.getModifiedReminder(i) != 0) {
  2486. hr = HrParseReminder(lRemindBefore, ttReminderTime, false, &lpicComp);
  2487. if (hr == hrSuccess)
  2488. icalcomponent_add_component(lpicException.get(), lpicComp);
  2489. }
  2490. }
  2491. if (ulModifications & ARO_LOCATION) {
  2492. lpicProp = icalcomponent_get_first_property(lpicException.get(), ICAL_LOCATION_PROPERTY);
  2493. if (lpicProp) {
  2494. icalcomponent_remove_property(lpicException.get(), lpicProp);
  2495. icalproperty_free(lpicProp);
  2496. }
  2497. const wstring wstrTmp = cRecurrence.getModifiedLocation(i);
  2498. icalcomponent_add_property(lpicException.get(), icalproperty_new_location(m_converter.convert_to<std::string>(m_strCharset.c_str(), wstrTmp, rawsize(wstrTmp), CHARSET_WCHAR).c_str()));
  2499. }
  2500. if (ulModifications & ARO_BUSYSTATUS) {
  2501. // new X-MICROSOFT-CDO-INTENDEDSTATUS and TRANSP
  2502. lpicProp = icalcomponent_get_first_property(lpicException.get(), ICAL_TRANSP_PROPERTY);
  2503. if (lpicProp) {
  2504. icalcomponent_remove_property(lpicException.get(), lpicProp);
  2505. icalproperty_free(lpicProp);
  2506. }
  2507. lpicProp = icalcomponent_get_first_property(lpicException.get(), ICAL_X_PROPERTY);
  2508. while (lpicProp && (strcmp(icalproperty_get_x_name(lpicProp), "X-MICROSOFT-CDO-INTENDEDSTATUS") != 0))
  2509. lpicProp = icalcomponent_get_next_property(lpicException.get(), ICAL_X_PROPERTY);
  2510. if (lpicProp) {
  2511. icalcomponent_remove_property(lpicException.get(), lpicProp);
  2512. icalproperty_free(lpicProp);
  2513. }
  2514. HrSetBusyStatus(lpException, cRecurrence.getModifiedBusyStatus(i), lpicException.get());
  2515. }
  2516. if (ulModifications & ARO_ATTACHMENT) {
  2517. // ..?
  2518. }
  2519. if (ulModifications & ARO_APPTCOLOR) {
  2520. // should never happen, according to the specs
  2521. }
  2522. if (ulModifications & ARO_EXCEPTIONAL_BODY) {
  2523. lpicProp = icalcomponent_get_first_property(lpicException.get(), ICAL_DESCRIPTION_PROPERTY);
  2524. if (lpicProp) {
  2525. icalcomponent_remove_property(lpicException.get(), lpicProp);
  2526. icalproperty_free(lpicProp);
  2527. }
  2528. lpicProp = NULL;
  2529. if (HrSetBody(lpException, &lpicProp) == hrSuccess)
  2530. icalcomponent_add_property(lpicException.get(), lpicProp);
  2531. }
  2532. lstExceptions.push_back(lpicException.release());
  2533. }
  2534. *lpEventList = std::move(lstExceptions);
  2535. return hr;
  2536. }
  2537. /**
  2538. * Update the VALARM component from exception reminder
  2539. *
  2540. * @param[in] lpicEvent ical component whose reminder is to be updated
  2541. * @param[in] lReminder new reminder time
  2542. * @return MAPI error code
  2543. * @retval MAPI_E_NOT_FOUND no VALARM component found in ical component
  2544. */
  2545. HRESULT VConverter::HrUpdateReminderTime(icalcomponent *lpicEvent, LONG lReminder)
  2546. {
  2547. icalcomponent *lpicAlarm = NULL;
  2548. icalproperty *lpicProp = NULL;
  2549. icaltriggertype sittTrigger;
  2550. lpicAlarm = icalcomponent_get_first_component(lpicEvent, ICAL_VALARM_COMPONENT);
  2551. if (lpicAlarm == NULL)
  2552. return MAPI_E_NOT_FOUND;
  2553. memset(&sittTrigger, 0, sizeof(icaltriggertype));
  2554. sittTrigger.duration = icaldurationtype_from_int(-1 * lReminder * 60); // set seconds
  2555. lpicProp = icalcomponent_get_first_property(lpicAlarm, ICAL_TRIGGER_PROPERTY);
  2556. if (lpicProp) {
  2557. icalcomponent_remove_property(lpicAlarm, lpicProp);
  2558. icalproperty_free(lpicProp);
  2559. }
  2560. icalcomponent_add_property(lpicAlarm, icalproperty_new_trigger(sittTrigger));
  2561. return hrSuccess;
  2562. }
  2563. /**
  2564. * Returns the exeception mapi message of the corresponding base date
  2565. *
  2566. * @param[in] lpMessage Mapi message to be converted to ical
  2567. * @param[in] tStart Base date of the exception
  2568. * @param[out] lppMessage Returned exception mapi message
  2569. *
  2570. * @return MAPI error code
  2571. * @retval MAPI_E_NOT_FOUND No exception found
  2572. */
  2573. HRESULT VConverter::HrGetExceptionMessage(LPMESSAGE lpMessage, time_t tStart, LPMESSAGE *lppMessage)
  2574. {
  2575. HRESULT hr = hrSuccess;
  2576. object_ptr<IMAPITable> lpAttachTable;
  2577. rowset_ptr lpRows;
  2578. const SPropValue *lpPropVal = nullptr;
  2579. object_ptr<IAttach> lpAttach;
  2580. LPMESSAGE lpAttachedMessage = NULL;
  2581. SPropValue sStart = {0};
  2582. SPropValue sMethod = {0};
  2583. sStart.ulPropTag = PR_EXCEPTION_STARTTIME;
  2584. UnixTimeToFileTime(tStart, &sStart.Value.ft);
  2585. sMethod.ulPropTag = PR_ATTACH_METHOD;
  2586. sMethod.Value.ul = ATTACH_EMBEDDED_MSG;
  2587. hr = lpMessage->GetAttachmentTable(0, &~lpAttachTable);
  2588. if (hr != hrSuccess)
  2589. return hr;
  2590. // restrict to only exception attachments
  2591. hr = ECAndRestriction(
  2592. ECExistRestriction(sStart.ulPropTag) +
  2593. ECPropertyRestriction(RELOP_EQ, sStart.ulPropTag, &sStart, ECRestriction::Cheap) +
  2594. ECExistRestriction(sMethod.ulPropTag) +
  2595. ECPropertyRestriction(RELOP_EQ, sMethod.ulPropTag, &sMethod, ECRestriction::Cheap)
  2596. ).RestrictTable(lpAttachTable, 0);
  2597. if (hr != hrSuccess)
  2598. return hr;
  2599. // should result in 1 attachment
  2600. hr = lpAttachTable->QueryRows(-1, 0, &~lpRows);
  2601. if (hr != hrSuccess)
  2602. return hr;
  2603. if (lpRows->cRows == 0)
  2604. // if this is a cancel message, no exceptions are present, so ignore.
  2605. return MAPI_E_NOT_FOUND;
  2606. lpPropVal = PCpropFindProp(lpRows->aRow[0].lpProps, lpRows->aRow[0].cValues, PR_ATTACH_NUM);
  2607. if (lpPropVal == nullptr)
  2608. return MAPI_E_NOT_FOUND;
  2609. hr = lpMessage->OpenAttach(lpPropVal->Value.ul, nullptr, 0, &~lpAttach);
  2610. if (hr != hrSuccess)
  2611. return hr;
  2612. hr = lpAttach->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, 0, (LPUNKNOWN *)&lpAttachedMessage);
  2613. if (hr != hrSuccess)
  2614. return hr;
  2615. *lppMessage = lpAttachedMessage;
  2616. return hrSuccess;
  2617. }
  2618. /**
  2619. * Converts the timezone parameter from an ical time property to MAPI
  2620. * properties, and saves this in the given lpIcalItem object.
  2621. *
  2622. * @todo this is a ical -> mapi conversion util function, so move to block with all ical->mapi code.
  2623. *
  2624. * @param[in] lpicProp The ical time property to finc the timezone in
  2625. * @param[in,out] lpIcalItem This object is modified
  2626. *
  2627. * @return MAPI error code
  2628. */
  2629. HRESULT VConverter::HrAddTimeZone(icalproperty *lpicProp, icalitem *lpIcalItem)
  2630. {
  2631. icalparameter* lpicTZParam = NULL;
  2632. const char *lpszTZID = NULL;
  2633. std::string strTZ;
  2634. SPropValue sPropVal;
  2635. // Take the timezone from DTSTART and set that as the item timezone
  2636. lpicTZParam = icalproperty_get_first_parameter(lpicProp, ICAL_TZID_PARAMETER);
  2637. // All day recurring items may not have timezone data.
  2638. if (lpicTZParam == NULL && lpicProp == NULL)
  2639. return hrSuccess;
  2640. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TIMEZONE], PT_UNICODE);
  2641. if (lpicTZParam != NULL) {
  2642. strTZ = urlDecode(icalparameter_get_tzid(lpicTZParam));
  2643. lpszTZID = strTZ.c_str();
  2644. } else if (!m_mapTimeZones->empty()) {
  2645. lpszTZID = (m_mapTimeZones->begin()->first).c_str();
  2646. } else {
  2647. return hrSuccess;
  2648. }
  2649. HrCopyString(m_converter, m_strCharset, lpIcalItem->base, lpszTZID, &sPropVal.Value.lpszW);
  2650. lpIcalItem->lstMsgProps.push_back(sPropVal);
  2651. // keep found timezone also as current timezone. will be used in recurrence
  2652. m_iCurrentTimeZone = m_mapTimeZones->find(lpszTZID);
  2653. if (m_iCurrentTimeZone == m_mapTimeZones->cend())
  2654. //.. huh? did find a timezone id, but not the actual timezone?? FAIL!
  2655. return MAPI_E_NOT_FOUND;
  2656. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TIMEZONEDATA], PT_BINARY);
  2657. sPropVal.Value.bin.cb = sizeof(TIMEZONE_STRUCT);
  2658. HRESULT hr = MAPIAllocateMore(sizeof(TIMEZONE_STRUCT), lpIcalItem->base, (void**)&sPropVal.Value.bin.lpb);
  2659. if (hr != hrSuccess)
  2660. return hr;
  2661. memcpy(sPropVal.Value.bin.lpb, &m_iCurrentTimeZone->second, sizeof(TIMEZONE_STRUCT));
  2662. lpIcalItem->lstMsgProps.push_back(sPropVal);
  2663. // save timezone in icalitem
  2664. lpIcalItem->tTZinfo = m_iCurrentTimeZone->second;
  2665. return hrSuccess;
  2666. }
  2667. /**
  2668. * Returns the Allday Status from the ical data as a boolean.
  2669. *
  2670. * Checks for "DTSTART" weather it contains a date, and sets the all
  2671. * day status as true. If that property was not found then checks if
  2672. * "X-MICROSOFT-CDO-ALLDAYEVENT" property to set the all day status.
  2673. *
  2674. * @param[in] lpicEvent VEVENT ical component
  2675. * @param[out] lpblIsAllday Return variable to for allday status
  2676. * @return Always returns hrSuccess
  2677. */
  2678. HRESULT VConverter::HrRetrieveAlldayStatus(icalcomponent *lpicEvent, bool *lpblIsAllday)
  2679. {
  2680. icalproperty *lpicProp = NULL;
  2681. icaltimetype icStart;
  2682. icaltimetype icEnd;
  2683. bool blIsAllday = false;
  2684. // Note: we do not set bIsAllDay to true when (END-START)%24h == 0
  2685. // If the user forced his ICAL client not to set this to 'true', it really wants an item that is a multiple of 24h, but specify the times too.
  2686. icStart = icalcomponent_get_dtstart(lpicEvent);
  2687. if (icStart.is_date)
  2688. {
  2689. *lpblIsAllday = true;
  2690. return hrSuccess;
  2691. }
  2692. // only assume the X header valid when it's a non-floating timestamp.
  2693. // also check is_utc and/or zone pointer in DTSTART/DTEND ?
  2694. icEnd = icalcomponent_get_dtend(lpicEvent);
  2695. if (icStart.hour + icStart.minute + icStart.second != 0 ||
  2696. icEnd.hour + icEnd.minute + icEnd.second != 0) {
  2697. *lpblIsAllday = false;
  2698. return hrSuccess;
  2699. }
  2700. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_X_PROPERTY);
  2701. while (lpicProp) {
  2702. if (strcmp(icalproperty_get_x_name(lpicProp), "X-MICROSOFT-CDO-ALLDAYEVENT") == 0){
  2703. blIsAllday = strcmp(icalproperty_get_x(lpicProp),"TRUE") == 0;
  2704. break;
  2705. }
  2706. lpicProp = icalcomponent_get_next_property(lpicEvent, ICAL_X_PROPERTY);
  2707. }
  2708. *lpblIsAllday = blIsAllday;
  2709. return hrSuccess;
  2710. }
  2711. /**
  2712. * Handles mapi to ical conversion of message with recurrence and
  2713. * exceptions. This is the entrypoint of the class.
  2714. *
  2715. * @param[in] lpMessage Mapi message to be converted to ical
  2716. * @param[out] lpicMethod The ical method set in the ical data
  2717. * @param[out] lpEventList List of ical components retuned after ical conversion
  2718. * @return MAPI error code
  2719. */
  2720. HRESULT VConverter::HrMAPI2ICal(LPMESSAGE lpMessage, icalproperty_method *lpicMethod, std::list<icalcomponent*> *lpEventList)
  2721. {
  2722. HRESULT hr = hrSuccess;
  2723. std::list<icalcomponent*> lstEvents;
  2724. icalproperty_method icMainMethod = ICAL_METHOD_NONE;
  2725. icalcomp_ptr lpicEvent;
  2726. memory_ptr<SPropValue> lpSpropValArray;
  2727. std::unique_ptr<icaltimezone, icalmapi_delete> lpicTZinfo;
  2728. std::string strTZid;
  2729. ULONG cbSize = 0;
  2730. SizedSPropTagArray(3, proptags) = {3,
  2731. {CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_RECURRING], PT_BOOLEAN),
  2732. CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_ISRECURRING], PT_BOOLEAN),
  2733. CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_ISRECURRING], PT_BOOLEAN)}};
  2734. // handle toplevel
  2735. hr = HrMAPI2ICal(lpMessage, &icMainMethod, &unique_tie(lpicTZinfo),
  2736. &strTZid, &unique_tie(lpicEvent));
  2737. if (hr != hrSuccess)
  2738. return hr;
  2739. cbSize = 0;
  2740. hr = lpMessage->GetProps(proptags, 0, &cbSize, &~lpSpropValArray);
  2741. if (FAILED(hr))
  2742. return hrSuccess;
  2743. hr = hrSuccess;
  2744. // if recurring, add recurrence. We have to check two props since CDO only sets the second, while Outlook only sets the first :S
  2745. if (((PROP_TYPE(lpSpropValArray[0].ulPropTag) != PT_ERROR) &&
  2746. lpSpropValArray[0].Value.b == TRUE) ||
  2747. ((PROP_TYPE(lpSpropValArray[1].ulPropTag) != PT_ERROR) &&
  2748. lpSpropValArray[1].Value.b == TRUE) ||
  2749. ((PROP_TYPE(lpSpropValArray[2].ulPropTag) != PT_ERROR) &&
  2750. lpSpropValArray[2].Value.b == TRUE))
  2751. {
  2752. hr = HrSetRecurrence(lpMessage, lpicEvent.get(),
  2753. lpicTZinfo.get(), strTZid, &lstEvents);
  2754. if (hr != hrSuccess)
  2755. return hr;
  2756. }
  2757. // push the main event in the front, before all exceptions
  2758. lstEvents.push_front(lpicEvent.release());
  2759. // end
  2760. *lpicMethod = icMainMethod;
  2761. *lpEventList = std::move(lstEvents);
  2762. return hr;
  2763. }
  2764. /**
  2765. * The actual MAPI to ical conversion for mapi message excluding
  2766. * recurrence. This function is used to implement both VTodo and
  2767. * VEvent conversion.
  2768. *
  2769. * @param[in] lpMessage Mapi message to be converted to ical
  2770. * @param[out] lpicMethod The ical method set in the ical data
  2771. * @param[out] lppicTZinfo ical timezone
  2772. * @param[out] lpstrTZid timezone name
  2773. * @param[out] lpEvent ical component containing ical data
  2774. * @return MAPI error code
  2775. */
  2776. HRESULT VConverter::HrMAPI2ICal(LPMESSAGE lpMessage, icalproperty_method *lpicMethod, icaltimezone **lppicTZinfo, std::string *lpstrTZid, icalcomponent *lpEvent)
  2777. {
  2778. HRESULT hr = hrSuccess;
  2779. icalproperty_method icMethod = ICAL_METHOD_NONE;
  2780. icalproperty *lpProp = NULL;
  2781. const SPropValue *lpPropVal = nullptr;
  2782. memory_ptr<SPropValue> lpMsgProps;
  2783. ULONG ulMsgProps = 0;
  2784. TIMEZONE_STRUCT ttTZinfo = {0};
  2785. icaltimezone *lpicTZinfo = NULL;
  2786. ULONG ulCount = 0;
  2787. std::string strTZid;
  2788. std::string strUid;
  2789. std::wstring wstrBuf;
  2790. hr = lpMessage->GetProps(NULL, MAPI_UNICODE, &ulMsgProps, &~lpMsgProps);
  2791. if (FAILED(hr))
  2792. return hr;
  2793. hr = HrFindTimezone(ulMsgProps, lpMsgProps, &strTZid, &ttTZinfo, &lpicTZinfo);
  2794. if (hr != hrSuccess)
  2795. return hr;
  2796. // non-UTC timezones are placed in the map, and converted using HrCreateVTimeZone() in MAPIToICal.cpp
  2797. if(!m_bCensorPrivate) {
  2798. // not an exception, so parent message is the message itself
  2799. hr = HrSetOrganizerAndAttendees(lpMessage, lpMessage, ulMsgProps, lpMsgProps, &icMethod, lpEvent);
  2800. if (hr != hrSuccess)
  2801. return hr;
  2802. }
  2803. // Set show_time_as / TRANSP
  2804. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_BUSYSTATUS], PT_LONG));
  2805. if (!m_bCensorPrivate && lpPropVal)
  2806. HrSetBusyStatus(lpMessage, lpPropVal->Value.ul, lpEvent);
  2807. hr = HrSetTimeProperties(lpMsgProps, ulMsgProps, lpicTZinfo, strTZid, lpEvent);
  2808. if (hr != hrSuccess)
  2809. return hr;
  2810. // Set RECURRENCE-ID for exception
  2811. hr = HrSetRecurrenceID(lpMsgProps, ulMsgProps, lpicTZinfo, strTZid, lpEvent);
  2812. if (hr != hrSuccess)
  2813. return hr;
  2814. // Set subject / SUMMARY
  2815. if(m_bCensorPrivate) {
  2816. lpProp = icalproperty_new_summary("Private Appointment");
  2817. icalcomponent_add_property(lpEvent, lpProp);
  2818. }
  2819. else {
  2820. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_SUBJECT_W);
  2821. if (lpPropVal && lpPropVal->Value.lpszW[0] != '\0') {
  2822. lpProp = icalproperty_new_summary(m_converter.convert_to<string>(m_strCharset.c_str(), lpPropVal->Value.lpszW, rawsize(lpPropVal->Value.lpszW), CHARSET_WCHAR).c_str());
  2823. icalcomponent_add_property(lpEvent, lpProp);
  2824. }
  2825. }
  2826. // Set location / LOCATION
  2827. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_LOCATION], PT_UNICODE));
  2828. if (!m_bCensorPrivate && lpPropVal && lpPropVal->Value.lpszW[0] != '\0') {
  2829. lpProp = icalproperty_new_location(m_converter.convert_to<string>(m_strCharset.c_str(), lpPropVal->Value.lpszW, rawsize(lpPropVal->Value.lpszW), CHARSET_WCHAR).c_str());
  2830. icalcomponent_add_property(lpEvent, lpProp);
  2831. }
  2832. // Set body / DESCRIPTION
  2833. if(!m_bCensorPrivate) {
  2834. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_BODY_W);
  2835. if (lpPropVal && lpPropVal->Value.lpszW[0] != '\0') {
  2836. std::wstring strBody;
  2837. // The body is converted as OL2003 does not parse '\r' & '\t' correctly
  2838. // Newer versions also have some issues parsing there chars
  2839. StringTabtoSpaces(lpPropVal->Value.lpszW, &strBody);
  2840. StringCRLFtoLF(strBody, &strBody);
  2841. lpProp = icalproperty_new_description(m_converter.convert_to<string>(m_strCharset.c_str(), lpPropVal->Value.lpszW, rawsize(lpPropVal->Value.lpszW), CHARSET_WCHAR).c_str());
  2842. } else {
  2843. hr = HrSetBody(lpMessage, &lpProp);
  2844. }
  2845. if (hr == hrSuccess)
  2846. icalcomponent_add_property(lpEvent, lpProp);
  2847. hr = hrSuccess;
  2848. }
  2849. // Set priority - use PR_IMPORTANCE or PR_PRIORITY
  2850. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_IMPORTANCE);
  2851. if (!m_bCensorPrivate && lpPropVal) {
  2852. lpProp = icalproperty_new_priority(5 - ((lpPropVal->Value.l - 1) * 4));
  2853. icalcomponent_add_property(lpEvent, lpProp);
  2854. } else {
  2855. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_PRIORITY);
  2856. if (!m_bCensorPrivate && lpPropVal && lpPropVal->Value.l != 0) {
  2857. lpProp = icalproperty_new_priority(5 - (lpPropVal->Value.l * 4));
  2858. icalcomponent_add_property(lpEvent, lpProp);
  2859. }
  2860. }
  2861. // Set keywords / CATEGORIES
  2862. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_KEYWORDS], PT_MV_UNICODE));
  2863. if (lpPropVal && lpPropVal->Value.MVszA.cValues > 0) {
  2864. // The categories need to be comma-separated
  2865. wstrBuf.reserve(lpPropVal->Value.MVszW.cValues * 50); // 50 chars per category is a wild guess, but more than enough
  2866. for (ulCount = 0; ulCount < lpPropVal->Value.MVszW.cValues; ++ulCount) {
  2867. if (ulCount)
  2868. wstrBuf += L",";
  2869. wstrBuf += lpPropVal->Value.MVszW.lppszW[ulCount];
  2870. }
  2871. if (!wstrBuf.empty()) {
  2872. lpProp = icalproperty_new_categories(m_converter.convert_to<string>(m_strCharset.c_str(), wstrBuf, rawsize(wstrBuf), CHARSET_WCHAR).c_str());
  2873. icalcomponent_add_property(lpEvent, lpProp);
  2874. }
  2875. }
  2876. // Set url
  2877. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_NETSHOWURL], PT_UNICODE));
  2878. if (lpPropVal && lpPropVal->Value.lpszW[0] != '\0') {
  2879. lpProp = icalproperty_new_url(m_converter.convert_to<string>(m_strCharset.c_str(), lpPropVal->Value.lpszW, rawsize(lpPropVal->Value.lpszW), CHARSET_WCHAR).c_str());
  2880. icalcomponent_add_property(lpEvent, lpProp);
  2881. }
  2882. // Set contacts
  2883. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_CONTACTS], PT_MV_UNICODE));
  2884. if (lpPropVal) {
  2885. for (ulCount = 0; ulCount < lpPropVal->Value.MVszW.cValues; ++ulCount) {
  2886. lpProp = icalproperty_new_contact(m_converter.convert_to<string>(m_strCharset.c_str(), lpPropVal->Value.MVszW.lppszW[ulCount], rawsize(lpPropVal->Value.MVszW.lppszW[ulCount]), CHARSET_WCHAR).c_str());
  2887. icalcomponent_add_property(lpEvent, lpProp);
  2888. }
  2889. }
  2890. // Set sensivity / CLASS
  2891. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_SENSITIVITY);
  2892. if (lpPropVal) {
  2893. switch (lpPropVal->Value.ul) {
  2894. case 1: //Personal
  2895. case 2: //Private
  2896. lpProp = icalproperty_new_class(ICAL_CLASS_PRIVATE);
  2897. break;
  2898. case 3: //CompanyConfidential
  2899. lpProp = icalproperty_new_class(ICAL_CLASS_CONFIDENTIAL);
  2900. break;
  2901. default:
  2902. lpProp = icalproperty_new_class(ICAL_CLASS_PUBLIC);
  2903. break;
  2904. }
  2905. icalcomponent_add_property(lpEvent, lpProp);
  2906. }
  2907. // Set and/or create UID
  2908. // Global Object ID
  2909. // In Microsoft Office Outlook 2003 Service Pack 1 (SP1) and earlier versions, the Global Object ID is generated when an organizer first sends a meeting request.
  2910. // Earlier versions of Outlook do not generate a Global Object ID for unsent meetings or for appointments that have no recipients.
  2911. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_GOID], PT_BINARY));
  2912. if (lpPropVal == NULL)
  2913. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_CLEANID], PT_BINARY));
  2914. // If lpPropVal is 0, the global object id and cleanglobal id haven't been found.
  2915. // The iCal UID is saved into both the global object id and cleanglobal id.
  2916. if (lpPropVal == NULL) {
  2917. SPropValue propUid;
  2918. hr = HrGenerateUid(&strUid);
  2919. if (hr != hrSuccess)
  2920. return hr;
  2921. hr = HrMakeBinaryUID(strUid, lpMsgProps, &propUid); // base is lpMsgProps, which will be freed later
  2922. // Set global object id and cleanglobal id.
  2923. // ignore write errors, not really required that these properties are saved
  2924. propUid.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_GOID], PT_BINARY);
  2925. HrSetOneProp(lpMessage, &propUid);
  2926. propUid.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_CLEANID], PT_BINARY);
  2927. HrSetOneProp(lpMessage, &propUid);
  2928. // if we cannot write the message, use PR_ENTRYID to have the same uid every time we return the item
  2929. // otherwise, ignore the error
  2930. hr = lpMessage->SaveChanges(KEEP_OPEN_READWRITE);
  2931. if (hr == E_ACCESSDENIED) {
  2932. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, PR_ENTRYID);
  2933. if (lpPropVal)
  2934. strUid = bin2hex(lpPropVal->Value.bin.cb,lpPropVal->Value.bin.lpb);
  2935. }
  2936. hr = hrSuccess;
  2937. } else {
  2938. HrGetICalUidFromBinUid(lpPropVal->Value.bin, &strUid);
  2939. }
  2940. if(IsOutlookUid(strUid))
  2941. strUid.replace(32, 8, "00000000");
  2942. lpProp = icalproperty_new_uid(strUid.c_str());
  2943. icalcomponent_add_property(lpEvent,lpProp);
  2944. hr = HrSetItemSpecifics(ulMsgProps, lpMsgProps, lpEvent);
  2945. if (hr != hrSuccess)
  2946. return hr;
  2947. //Sequence
  2948. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_APPTSEQNR], PT_LONG));
  2949. if(lpPropVal)
  2950. {
  2951. lpProp = icalproperty_new_sequence(lpPropVal->Value.ul);
  2952. icalcomponent_add_property(lpEvent, lpProp);
  2953. }
  2954. // Set alarm / VALARM (if alarm is found in lpMessage)
  2955. if(!m_bCensorPrivate) {
  2956. hr = HrSetVAlarm(ulMsgProps, lpMsgProps, lpEvent);
  2957. if (hr != hrSuccess)
  2958. return hr;
  2959. }
  2960. // Set X-Properties.
  2961. hr = HrSetXHeaders(ulMsgProps, lpMsgProps, lpMessage, lpEvent);
  2962. if (hr != hrSuccess)
  2963. return hr;
  2964. // set return values
  2965. if (lpicMethod)
  2966. *lpicMethod = icMethod;
  2967. if (lppicTZinfo)
  2968. *lppicTZinfo = lpicTZinfo;
  2969. if (lpstrTZid)
  2970. *lpstrTZid = std::move(strTZid);
  2971. return hrSuccess;
  2972. }
  2973. } /* namespace */