mailer.cpp 92 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318
  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 "mailer.h"
  20. #include "archive.h"
  21. #include <mapitags.h>
  22. #include <kopano/mapiext.h>
  23. #include <kopano/memory.hpp>
  24. #include <kopano/tie.hpp>
  25. #include <mapiutil.h>
  26. #include <mapidefs.h>
  27. #include <mapix.h>
  28. #include <mapi.h>
  29. #include <kopano/ECLogger.h>
  30. #include <kopano/ECRestriction.h>
  31. #include <kopano/ECConfig.h>
  32. #include <kopano/IECUnknown.h>
  33. #include <kopano/ecversion.h>
  34. #include <kopano/IECSecurity.h>
  35. #include <kopano/IECServiceAdmin.h>
  36. #include <kopano/MAPIErrors.h>
  37. #include "IECSpooler.h"
  38. #include <kopano/ECGuid.h>
  39. #include <edkguid.h>
  40. #include <kopano/CommonUtil.h>
  41. #include <kopano/Util.h>
  42. #include <kopano/stringutil.h>
  43. #include "mapicontact.h"
  44. #include <kopano/mapiguidext.h>
  45. #include <kopano/EMSAbTag.h>
  46. #include <kopano/ECABEntryID.h>
  47. #include <kopano/ECGetText.h>
  48. #include <kopano/charset/convert.h>
  49. #include <kopano/charset/convstring.h>
  50. #include "PyMapiPlugin.h"
  51. #include <list>
  52. #include <algorithm>
  53. #include "spmain.h"
  54. using namespace std;
  55. using namespace KCHL;
  56. /**
  57. * Expand all rows in the lpTable to normal user recipient
  58. * entries. When a group is expanded from a group, this function will
  59. * be called recursively.
  60. *
  61. * @param[in] lpAddrBook The Global Addressbook of the user sending lpMessage
  62. * @param[in] lpMessage The message to expand groups for
  63. * @param[in] lpTable The restricted recipient table of lpMessage
  64. * @param[in] lpEntryRestriction The restriction used on lpTable
  65. * @param[in] ulRecipType The recipient type (To/Cc/Bcc), default is MAPI_TO
  66. * @param[in] lpExpandedGroups List of EntryIDs of groups already expanded. Double groups will just be removed.
  67. * @param[in] recurrence true if this function should recurse further.
  68. */
  69. static HRESULT ExpandRecipientsRecursive(LPADRBOOK lpAddrBook,
  70. IMessage *lpMessage, IMAPITable *lpTable,
  71. LPSRestriction lpEntryRestriction, ULONG ulRecipType,
  72. list<SBinary> *lpExpandedGroups, bool recurrence = true)
  73. {
  74. HRESULT hr = hrSuccess;
  75. ULONG ulObj = 0;
  76. bool bExpandSub = recurrence;
  77. static constexpr const SizedSPropTagArray(7, sptaColumns) =
  78. {7, {PR_ROWID, PR_DISPLAY_NAME_W, PR_SMTP_ADDRESS_W,
  79. PR_RECIPIENT_TYPE, PR_OBJECT_TYPE, PR_DISPLAY_TYPE, PR_ENTRYID}};
  80. hr = lpTable->SetColumns(sptaColumns, 0);
  81. if (hr != hrSuccess) {
  82. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipientsRecursive(): SetColumns failed %x", hr);
  83. return hr;
  84. }
  85. while (true) {
  86. memory_ptr<SPropValue> lpSMTPAddress;
  87. rowset_ptr lpsRowSet;
  88. /* Request group from table */
  89. hr = lpTable->QueryRows(1, 0, &~lpsRowSet);
  90. if (hr != hrSuccess) {
  91. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipientsRecursive(): QueryRows failed %x", hr);
  92. return hr;
  93. }
  94. if (lpsRowSet->cRows != 1)
  95. break;
  96. /* From this point on we use 'continue' when something fails,
  97. * since all errors are related to the current entry and we should
  98. * make sure we resolve as many recipients as possible. */
  99. auto lpRowId = PCpropFindProp(lpsRowSet->aRow[0].lpProps, lpsRowSet->aRow[0].cValues, PR_ROWID);
  100. auto lpEntryId = PCpropFindProp(lpsRowSet->aRow[0].lpProps, lpsRowSet->aRow[0].cValues, PR_ENTRYID);
  101. auto lpDisplayType = PCpropFindProp(lpsRowSet->aRow[0].lpProps, lpsRowSet->aRow[0].cValues, PR_DISPLAY_TYPE);
  102. auto lpObjectType = PCpropFindProp(lpsRowSet->aRow[0].lpProps, lpsRowSet->aRow[0].cValues, PR_OBJECT_TYPE);
  103. auto lpRecipType = PCpropFindProp(lpsRowSet->aRow[0].lpProps, lpsRowSet->aRow[0].cValues, PR_RECIPIENT_TYPE);
  104. auto lpDisplayName = PCpropFindProp(lpsRowSet->aRow[0].lpProps, lpsRowSet->aRow[0].cValues, PR_DISPLAY_NAME_W);
  105. auto lpEmailAddress = PCpropFindProp(lpsRowSet->aRow[0].lpProps, lpsRowSet->aRow[0].cValues, PR_SMTP_ADDRESS_W);
  106. /* lpRowId, lpRecipType, and lpDisplayType are optional.
  107. * lpEmailAddress is only mandatory for MAPI_MAILUSER */
  108. if (!lpEntryId || !lpObjectType || !lpDisplayName)
  109. continue;
  110. /* By default we inherit the recipient type from parent */
  111. if (lpRecipType)
  112. ulRecipType = lpRecipType->Value.ul;
  113. if (lpObjectType->Value.ul == MAPI_MAILUSER) {
  114. if (!lpEmailAddress)
  115. continue;
  116. SizedADRLIST(1, sRowSMTProwSet);
  117. SPropValue p[4];
  118. sRowSMTProwSet.cEntries = 1;
  119. sRowSMTProwSet.aEntries[0].cValues = 4;
  120. sRowSMTProwSet.aEntries[0].rgPropVals = p;
  121. p[0].ulPropTag = PR_EMAIL_ADDRESS_W;
  122. p[0].Value.lpszW = lpEmailAddress->Value.lpszW;
  123. p[1].ulPropTag = PR_SMTP_ADDRESS_W;
  124. p[1].Value.lpszW = lpEmailAddress->Value.lpszW;
  125. p[2].ulPropTag = PR_RECIPIENT_TYPE;
  126. p[2].Value.ul = ulRecipType; /* Inherit from parent group */
  127. p[3].ulPropTag = PR_DISPLAY_NAME_W;
  128. p[3].Value.lpszW = lpDisplayName->Value.lpszW;
  129. hr = lpMessage->ModifyRecipients(MODRECIP_ADD, sRowSMTProwSet);
  130. if (hr != hrSuccess) {
  131. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to add e-mail address of %ls from group: %s (%x)",
  132. lpEmailAddress->Value.lpszW, GetMAPIErrorMessage(hr), hr);
  133. continue;
  134. }
  135. } else {
  136. SBinary sEntryId;
  137. object_ptr<IMAPITable> lpContentsTable;
  138. object_ptr<IDistList> lpDistlist;
  139. /* If we should recur further, just remove the group from the recipients list */
  140. if (!recurrence)
  141. goto remove_group;
  142. /* Only continue when this group has not yet been expanded previously */
  143. if (find(lpExpandedGroups->begin(), lpExpandedGroups->end(), lpEntryId->Value.bin) != lpExpandedGroups->end())
  144. goto remove_group;
  145. hr = lpAddrBook->OpenEntry(lpEntryId->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpEntryId->Value.bin.lpb), nullptr, 0, &ulObj, &~lpDistlist);
  146. if (hr != hrSuccess)
  147. continue;
  148. if(ulObj != MAPI_DISTLIST)
  149. continue;
  150. /* Never expand groups with an email address. The whole point of the email address is that it can be used
  151. * as a single entity */
  152. if (HrGetOneProp(lpDistlist, PR_SMTP_ADDRESS_W, &~lpSMTPAddress) == hrSuccess &&
  153. wcslen(lpSMTPAddress->Value.lpszW) > 0)
  154. continue;
  155. hr = lpDistlist->GetContentsTable(MAPI_UNICODE, &~lpContentsTable);
  156. if (hr != hrSuccess)
  157. continue;
  158. hr = lpContentsTable->Restrict(lpEntryRestriction, 0);
  159. if (hr != hrSuccess)
  160. continue;
  161. /* Group has been expanded (because we successfully have the contents table) time
  162. * to add it to our expanded group list. This has to be done or at least before the
  163. * recursive call to ExpandRecipientsRecursive().*/
  164. hr = Util::HrCopyEntryId(lpEntryId->Value.bin.cb, (LPENTRYID)lpEntryId->Value.bin.lpb,
  165. &sEntryId.cb, (LPENTRYID*)&sEntryId.lpb);
  166. lpExpandedGroups->push_back(sEntryId);
  167. /* Don't expand group Everyone or companies since both already contain all users
  168. * which should be put in the recipient list. */
  169. bExpandSub = !(((lpDisplayType) ? lpDisplayType->Value.ul == DT_ORGANIZATION : false) ||
  170. wcscasecmp(lpDisplayName->Value.lpszW, L"Everyone") == 0);
  171. // @todo find everyone using it's static entryid?
  172. /* Start/Continue recursion */
  173. hr = ExpandRecipientsRecursive(lpAddrBook, lpMessage, lpContentsTable,
  174. lpEntryRestriction, ulRecipType, lpExpandedGroups, bExpandSub);
  175. /* Ignore errors */
  176. remove_group:
  177. /* Only delete row when the rowid is present */
  178. if (!lpRowId)
  179. continue;
  180. hr = lpMessage->ModifyRecipients(MODRECIP_REMOVE,
  181. reinterpret_cast<ADRLIST *>(lpsRowSet.get()));
  182. if (hr != hrSuccess) {
  183. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to remove group %ls from recipient list: %s (%x).",
  184. lpDisplayName->Value.lpszW, GetMAPIErrorMessage(hr), hr);
  185. continue;
  186. }
  187. }
  188. }
  189. return hrSuccess;
  190. }
  191. /**
  192. * Expands groups in normal recipients.
  193. *
  194. * This function builds the restriction, and calls the recursion
  195. * function, since we can have group-in-groups.
  196. *
  197. * @todo use restriction macro's for readability.
  198. *
  199. * @param[in] lpAddrBook The Global Addressbook of the user sending lpMessage
  200. * @param[in] lpMessage The message to expand groups for.
  201. * @return HRESULT
  202. */
  203. static HRESULT ExpandRecipients(LPADRBOOK lpAddrBook, IMessage *lpMessage)
  204. {
  205. HRESULT hr = hrSuccess;
  206. list<SBinary> lExpandedGroups;
  207. object_ptr<IMAPITable> lpTable;
  208. memory_ptr<SRestriction> lpRestriction, lpEntryRestriction;
  209. /*
  210. * Setup group restriction:
  211. * PR_OBJECT_TYPE == MAPI_DISTLIST && PR_ADDR_TYPE == "ZARAFA"
  212. */
  213. hr = MAPIAllocateBuffer(sizeof(SRestriction), &~lpRestriction);
  214. if(hr != hrSuccess) {
  215. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipients(): MAPIAllocateBuffer failed %x", hr);
  216. goto exit;
  217. }
  218. hr = MAPIAllocateMore(sizeof(SRestriction) * 2, lpRestriction, (LPVOID*)&lpRestriction->res.resAnd.lpRes);
  219. if (hr != hrSuccess) {
  220. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipients(): MAPIAllocateMore failed(1) %x", hr);
  221. goto exit;
  222. }
  223. lpRestriction->rt = RES_AND;
  224. lpRestriction->res.resAnd.cRes = 2;
  225. hr = MAPIAllocateMore(sizeof(SPropValue), lpRestriction, (LPVOID*)&lpRestriction->res.resAnd.lpRes[0].res.resProperty.lpProp);
  226. if (hr != hrSuccess) {
  227. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipients(): MAPIAllocateMore failed(2) %x", hr);
  228. goto exit;
  229. }
  230. lpRestriction->res.resAnd.lpRes[0].rt = RES_PROPERTY;
  231. lpRestriction->res.resAnd.lpRes[0].res.resProperty.relop = RELOP_EQ;
  232. lpRestriction->res.resAnd.lpRes[0].res.resProperty.ulPropTag = PR_OBJECT_TYPE;
  233. lpRestriction->res.resAnd.lpRes[0].res.resProperty.lpProp->ulPropTag = PR_OBJECT_TYPE;
  234. lpRestriction->res.resAnd.lpRes[0].res.resProperty.lpProp->Value.ul = MAPI_DISTLIST;
  235. hr = MAPIAllocateMore(sizeof(SPropValue), lpRestriction, (LPVOID*)&lpRestriction->res.resAnd.lpRes[1].res.resProperty.lpProp);
  236. if (hr != hrSuccess) {
  237. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipients(): MAPIAllocateMore failed(3) %x", hr);
  238. goto exit;
  239. }
  240. lpRestriction->res.resAnd.lpRes[1].rt = RES_PROPERTY;
  241. lpRestriction->res.resAnd.lpRes[1].res.resProperty.relop = RELOP_EQ;
  242. lpRestriction->res.resAnd.lpRes[1].res.resProperty.ulPropTag = PR_ADDRTYPE_W;
  243. lpRestriction->res.resAnd.lpRes[1].res.resProperty.lpProp->ulPropTag = PR_ADDRTYPE_W;
  244. lpRestriction->res.resAnd.lpRes[1].res.resProperty.lpProp->Value.lpszW = const_cast<wchar_t *>(L"ZARAFA");
  245. /*
  246. * Setup entry restriction:
  247. * PR_ADDR_TYPE == "ZARAFA"
  248. */
  249. hr = MAPIAllocateBuffer(sizeof(SRestriction), &~lpEntryRestriction);
  250. if(hr != hrSuccess) {
  251. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipients(): MAPIAllocateBuffer failed %x", hr);
  252. goto exit;
  253. }
  254. hr = MAPIAllocateMore(sizeof(SPropValue), lpEntryRestriction, (LPVOID*)&lpEntryRestriction->res.resProperty.lpProp);
  255. if (hr != hrSuccess) {
  256. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipients(): MAPIAllocateMore failed(4) %x", hr);
  257. goto exit;
  258. }
  259. lpEntryRestriction->rt = RES_PROPERTY;
  260. lpEntryRestriction->res.resProperty.relop = RELOP_EQ;
  261. lpEntryRestriction->res.resProperty.ulPropTag = PR_ADDRTYPE_W;
  262. lpEntryRestriction->res.resProperty.lpProp->ulPropTag = PR_ADDRTYPE_W;
  263. lpEntryRestriction->res.resProperty.lpProp->Value.lpszW = const_cast<wchar_t *>(L"ZARAFA");
  264. hr = lpMessage->GetRecipientTable(MAPI_UNICODE, &~lpTable);
  265. if(hr != hrSuccess) {
  266. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipients(): GetRecipientTable failed %x", hr);
  267. goto exit;
  268. }
  269. /* The first table we send with ExpandRecipientsRecursive() is the RecipientTable itself,
  270. * we need to put a restriction on this table since the first time only the groups
  271. * should be added to the recipients list. Subsequent calls to ExpandRecipientsRecursive()
  272. * will send the group member table and will correct add the members to the recipients
  273. * table. */
  274. hr = lpTable->Restrict(lpRestriction, 0);
  275. if (hr != hrSuccess) {
  276. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipients(): Restrict failed %x", hr);
  277. goto exit;
  278. }
  279. /* ExpandRecipientsRecursive() will run recursively expanding each group
  280. * it finds including all subgroups. It will use the lExpandedGroups list
  281. * to protect itself for circular subgroup membership */
  282. hr = ExpandRecipientsRecursive(lpAddrBook, lpMessage, lpTable, lpEntryRestriction, MAPI_TO, &lExpandedGroups);
  283. if (hr != hrSuccess) {
  284. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ExpandRecipients(): ExpandRecipientsRecursive failed %x", hr);
  285. goto exit;
  286. }
  287. exit:
  288. for (const auto &g : lExpandedGroups)
  289. MAPIFreeBuffer(g.lpb);
  290. return hr;
  291. }
  292. /**
  293. * Rewrites a FAX:number "email address" to a sendable email address.
  294. *
  295. * @param[in] lpMAPISession The session of the user
  296. * @param[in] lpMessage The message to send
  297. * @return HRESULT
  298. */
  299. static HRESULT RewriteRecipients(LPMAPISESSION lpMAPISession,
  300. IMessage *lpMessage)
  301. {
  302. HRESULT hr = hrSuccess;
  303. object_ptr<IMAPITable> lpTable;
  304. memory_ptr<SPropTagArray> lpRecipColumns;
  305. const char *const lpszFaxDomain = g_lpConfig->GetSetting("fax_domain");
  306. const char *const lpszFaxInternational = g_lpConfig->GetSetting("fax_international");
  307. string strFaxMail;
  308. wstring wstrFaxMail, wstrOldFaxMail;
  309. ULONG ulObjType;
  310. ULONG cValues;
  311. // contab email_offset: 0: business, 1: home, 2: primary (outlook uses string 'other')
  312. static constexpr const SizedSPropTagArray(3, sptaFaxNumbers) =
  313. { 3, {PR_BUSINESS_FAX_NUMBER_A, PR_HOME_FAX_NUMBER_A,
  314. PR_PRIMARY_FAX_NUMBER_A}};
  315. if (!lpszFaxDomain || strcmp(lpszFaxDomain, "") == 0)
  316. return hr;
  317. hr = lpMessage->GetRecipientTable(MAPI_UNICODE, &~lpTable);
  318. if (hr != hrSuccess) {
  319. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RewriteRecipients(): GetRecipientTable failed %x", hr);
  320. return hr;
  321. }
  322. // we need all columns when rewriting FAX to SMTP
  323. hr = lpTable->QueryColumns(TBL_ALL_COLUMNS, &~lpRecipColumns);
  324. if (hr != hrSuccess) {
  325. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RewriteRecipients(): QueryColumns failed %x", hr);
  326. return hr;
  327. }
  328. hr = lpTable->SetColumns(lpRecipColumns, 0);
  329. if (hr != hrSuccess) {
  330. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RewriteRecipients(): SetColumns failed %x", hr);
  331. return hr;
  332. }
  333. while (TRUE) {
  334. rowset_ptr lpRowSet;
  335. hr = lpTable->QueryRows(1, 0, &~lpRowSet);
  336. if (hr != hrSuccess) {
  337. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RewriteRecipients(): QueryRows failed %x", hr);
  338. return hr;
  339. }
  340. if (lpRowSet->cRows == 0)
  341. break;
  342. auto lpEmailAddress = PpropFindProp(lpRowSet->aRow[0].lpProps, lpRowSet->aRow[0].cValues, PR_EMAIL_ADDRESS_W);
  343. auto lpEmailName = PCpropFindProp(lpRowSet->aRow[0].lpProps, lpRowSet->aRow[0].cValues, PR_DISPLAY_NAME_W);
  344. auto lpAddrType = PpropFindProp(lpRowSet->aRow[0].lpProps, lpRowSet->aRow[0].cValues, PR_ADDRTYPE_W);
  345. auto lpEntryID = PpropFindProp(lpRowSet->aRow[0].lpProps, lpRowSet->aRow[0].cValues, PR_ENTRYID);
  346. if (!(lpEmailAddress && lpAddrType && lpEntryID && lpEmailName))
  347. continue;
  348. if (wcscmp(lpAddrType->Value.lpszW, L"FAX") != 0)
  349. continue;
  350. // rewrite FAX address to <number>@<faxdomain>
  351. wstring wstrName, wstrType, wstrEmailAddress;
  352. memory_ptr<ENTRYID> lpNewEntryID;
  353. memory_ptr<SPropValue> lpFaxNumbers;
  354. ULONG cbNewEntryID;
  355. if (ECParseOneOff((LPENTRYID)lpEntryID->Value.bin.lpb, lpEntryID->Value.bin.cb, wstrName, wstrType, wstrEmailAddress) == hrSuccess) {
  356. // user entered manual fax address
  357. strFaxMail = convert_to<string>(wstrEmailAddress);
  358. } else {
  359. // check if entry is in contacts folder
  360. LPCONTAB_ENTRYID lpContabEntryID = (LPCONTAB_ENTRYID)lpEntryID->Value.bin.lpb;
  361. auto guid = reinterpret_cast<GUID *>(&lpContabEntryID->muid);
  362. // check validity of lpContabEntryID
  363. if (sizeof(CONTAB_ENTRYID) > lpEntryID->Value.bin.cb ||
  364. *guid != PSETID_CONTACT_FOLDER_RECIPIENT ||
  365. lpContabEntryID->email_offset < 3 ||
  366. lpContabEntryID->email_offset > 5)
  367. {
  368. /*hr = MAPI_E_INVALID_PARAMETER;*/
  369. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to convert FAX recipient, using %ls", lpEmailAddress->Value.lpszW);
  370. continue;
  371. }
  372. // 0..2 == reply to email offsets
  373. // 3..5 == fax email offsets
  374. lpContabEntryID->email_offset -= 3;
  375. object_ptr<IMailUser> lpFaxMailuser;
  376. hr = lpMAPISession->OpenEntry(lpContabEntryID->cbeid, reinterpret_cast<ENTRYID *>(lpContabEntryID->abeid), nullptr, 0, &ulObjType, &~lpFaxMailuser);
  377. if (hr != hrSuccess) {
  378. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to convert FAX recipient, using %ls: %s (%x)",
  379. lpEmailAddress->Value.lpszW, GetMAPIErrorMessage(hr), hr);
  380. continue;
  381. }
  382. hr = lpFaxMailuser->GetProps(sptaFaxNumbers, 0, &cValues, &~lpFaxNumbers);
  383. if (FAILED(hr)) {
  384. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to convert FAX recipient, using %ls: %s (%x)",
  385. lpEmailAddress->Value.lpszW, GetMAPIErrorMessage(hr), hr);
  386. continue;
  387. }
  388. if (lpFaxNumbers[lpContabEntryID->email_offset].ulPropTag != sptaFaxNumbers.aulPropTag[lpContabEntryID->email_offset]) {
  389. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "No suitable FAX number found, using %ls", lpEmailAddress->Value.lpszW);
  390. continue;
  391. }
  392. strFaxMail = lpFaxNumbers[lpContabEntryID->email_offset].Value.lpszA;
  393. }
  394. strFaxMail += string("@") + lpszFaxDomain;
  395. if (strFaxMail[0] == '+' && lpszFaxInternational != nullptr)
  396. strFaxMail = lpszFaxInternational + strFaxMail.substr(1, strFaxMail.length());
  397. wstrFaxMail = convert_to<wstring>(strFaxMail);
  398. wstrOldFaxMail = lpEmailAddress->Value.lpszW; // keep old string for logging
  399. // hack values in lpRowSet
  400. lpEmailAddress->Value.lpszW = (WCHAR*)wstrFaxMail.c_str();
  401. lpAddrType->Value.lpszW = const_cast<wchar_t *>(L"SMTP");
  402. // old value is stuck to the row allocation, so we can override it, but we also must free the new!
  403. ECCreateOneOff((LPTSTR)lpEmailName->Value.lpszW, (LPTSTR)L"SMTP", (LPTSTR)wstrFaxMail.c_str(), MAPI_UNICODE, &cbNewEntryID, &~lpNewEntryID);
  404. lpEntryID->Value.bin.lpb = reinterpret_cast<BYTE *>(lpNewEntryID.get());
  405. lpEntryID->Value.bin.cb = cbNewEntryID;
  406. hr = lpMessage->ModifyRecipients(MODRECIP_MODIFY,
  407. reinterpret_cast<ADRLIST *>(lpRowSet.get()));
  408. if (hr != hrSuccess) {
  409. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to set new FAX mail address for '%ls' to '%s': %s (%x)",
  410. wstrOldFaxMail.c_str(), strFaxMail.c_str(), GetMAPIErrorMessage(hr), hr);
  411. continue;
  412. }
  413. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Using new FAX mail address %s", strFaxMail.c_str());
  414. }
  415. return hrSuccess;
  416. }
  417. /**
  418. * Make the recipient table in the message unique. Key is the PR_SMTP_ADDRESS and PR_RECIPIENT_TYPE (To/Cc/Bcc).
  419. *
  420. * @param[in] lpMessage The message to fix the recipient table for.
  421. * @return HRESULT
  422. */
  423. static HRESULT UniqueRecipients(IMessage *lpMessage)
  424. {
  425. HRESULT hr = hrSuccess;
  426. object_ptr<IMAPITable> lpTable;
  427. string strEmail;
  428. ULONG ulRecipType = 0;
  429. static constexpr const SizedSPropTagArray(3, sptaColumns) =
  430. {3, {PR_ROWID, PR_SMTP_ADDRESS_A, PR_RECIPIENT_TYPE}};
  431. static constexpr const SizedSSortOrderSet(2, sosOrder) = {
  432. 2, 0, 0, {
  433. { PR_SMTP_ADDRESS_A, TABLE_SORT_ASCEND },
  434. { PR_RECIPIENT_TYPE, TABLE_SORT_ASCEND },
  435. }
  436. };
  437. hr = lpMessage->GetRecipientTable(0, &~lpTable);
  438. if (hr != hrSuccess)
  439. return hr;
  440. hr = lpTable->SetColumns(sptaColumns, 0);
  441. if (hr != hrSuccess)
  442. return hr;
  443. hr = lpTable->SortTable(sosOrder, 0);
  444. if (hr != hrSuccess)
  445. return hr;
  446. while (TRUE) {
  447. rowset_ptr lpRowSet;
  448. hr = lpTable->QueryRows(1, 0, &~lpRowSet);
  449. if (hr != hrSuccess)
  450. return hr;
  451. if (lpRowSet->cRows == 0)
  452. break;
  453. auto lpEmailAddress = PCpropFindProp(lpRowSet->aRow[0].lpProps, lpRowSet->aRow[0].cValues, PR_SMTP_ADDRESS_A);
  454. auto lpRecipType = PCpropFindProp(lpRowSet->aRow[0].lpProps, lpRowSet->aRow[0].cValues, PR_RECIPIENT_TYPE);
  455. if (!lpEmailAddress || !lpRecipType)
  456. continue;
  457. /* Filter To, Cc, Bcc individually */
  458. if (strEmail == lpEmailAddress->Value.lpszA && ulRecipType == lpRecipType->Value.ul) {
  459. hr = lpMessage->ModifyRecipients(MODRECIP_REMOVE,
  460. reinterpret_cast<ADRLIST *>(lpRowSet.get()));
  461. if (hr != hrSuccess)
  462. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to remove duplicate entry: %s (%x)",
  463. GetMAPIErrorMessage(hr), hr);
  464. } else {
  465. strEmail = string(lpEmailAddress->Value.lpszA);
  466. ulRecipType = lpRecipType->Value.ul;
  467. }
  468. }
  469. return hrSuccess;
  470. }
  471. static HRESULT RewriteQuotedRecipients(IMessage *lpMessage)
  472. {
  473. HRESULT hr = hrSuccess;
  474. object_ptr<IMAPITable> lpTable;
  475. wstring strEmail;
  476. static constexpr const SizedSPropTagArray(3, sptaColumns) =
  477. {3, {PR_ROWID, PR_EMAIL_ADDRESS_W, PR_RECIPIENT_TYPE}};
  478. hr = lpMessage->GetRecipientTable(0, &~lpTable);
  479. if (hr != hrSuccess) {
  480. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RewriteQuotedRecipients(): GetRecipientTable failed %x", hr);
  481. return hr;
  482. }
  483. hr = lpTable->SetColumns(sptaColumns, 0);
  484. if (hr != hrSuccess) {
  485. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RewriteQuotedRecipients(): SetColumns failed %x", hr);
  486. return hr;
  487. }
  488. while (TRUE) {
  489. rowset_ptr lpRowSet;
  490. hr = lpTable->QueryRows(1, 0, &~lpRowSet);
  491. if (hr != hrSuccess) {
  492. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RewriteQuotedRecipients(): QueryRows failed %x", hr);
  493. return hr;
  494. }
  495. if (lpRowSet->cRows == 0)
  496. break;
  497. auto lpEmailAddress = PpropFindProp(lpRowSet->aRow[0].lpProps, lpRowSet->aRow[0].cValues, PR_EMAIL_ADDRESS_W);
  498. auto lpRecipType = PCpropFindProp(lpRowSet->aRow[0].lpProps, lpRowSet->aRow[0].cValues, PR_RECIPIENT_TYPE);
  499. if (!lpEmailAddress || !lpRecipType)
  500. continue;
  501. strEmail = lpEmailAddress->Value.lpszW;
  502. if((strEmail[0] == '\'' && strEmail[strEmail.size()-1] == '\'') ||
  503. (strEmail[0] == '"' && strEmail[strEmail.size()-1] == '"')) {
  504. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Rewrite quoted recipient: %ls", strEmail.c_str());
  505. strEmail = strEmail.substr(1, strEmail.size()-2);
  506. lpEmailAddress->Value.lpszW = (WCHAR *)strEmail.c_str();
  507. hr = lpMessage->ModifyRecipients(MODRECIP_MODIFY,
  508. reinterpret_cast<ADRLIST *>(lpRowSet.get()));
  509. if (hr != hrSuccess) {
  510. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to rewrite quoted recipient: %s (%x)",
  511. GetMAPIErrorMessage(hr), hr);
  512. return hr;
  513. }
  514. }
  515. }
  516. return hrSuccess;
  517. }
  518. /**
  519. * Removes all MAPI_P1 marked recipients from a message.
  520. *
  521. * @param[in] lpMessage Message to remove MAPI_P1 recipients from
  522. * @return HRESULT
  523. */
  524. static HRESULT RemoveP1Recipients(IMessage *lpMessage)
  525. {
  526. HRESULT hr = hrSuccess;
  527. object_ptr<IMAPITable> lpTable;
  528. rowset_ptr lpRows;
  529. SPropValue sPropRestrict;
  530. sPropRestrict.ulPropTag = PR_RECIPIENT_TYPE;
  531. sPropRestrict.Value.ul = MAPI_P1;
  532. hr = lpMessage->GetRecipientTable(0, &~lpTable);
  533. if(hr != hrSuccess) {
  534. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RemoveP1Recipients(): GetRecipientTable failed %x", hr);
  535. return hr;
  536. }
  537. hr = ECPropertyRestriction(RELOP_EQ, PR_RECIPIENT_TYPE,
  538. &sPropRestrict, ECRestriction::Cheap).RestrictTable(lpTable, 0);
  539. if(hr != hrSuccess) {
  540. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RemoveP1Recipients(): Restrict failed %x", hr);
  541. return hr;
  542. }
  543. hr = lpTable->QueryRows(-1, 0, &~lpRows);
  544. if(hr != hrSuccess) {
  545. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RemoveP1Recipients(): QueryRows failed %x", hr);
  546. return hr;
  547. }
  548. hr = lpMessage->ModifyRecipients(MODRECIP_REMOVE, reinterpret_cast<ADRLIST *>(lpRows.get()));
  549. if (hr != hrSuccess)
  550. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "RemoveP1Recipients(): ModifyRecipients failed %x", hr);
  551. return hr;
  552. }
  553. /**
  554. * Creates an MDN message in the inbox of the given store for the passed message.
  555. *
  556. * This creates an MDN message in the inbox of the store passed, setting the correct properties and recipients. The most
  557. * important part of this function is to report errors of why sending failed. Sending can fail due to an overall problem
  558. * (when the entire message could not be sent) or when only some recipient didn't receive the message.
  559. *
  560. * In the case of partial failure (some recipients did not receive the email), the MDN message is populated with a recipient
  561. * table for all the recipients that failed. An error is attached to each of these recipients. The error information is
  562. * retrieved from the passed lpMailer object.
  563. *
  564. * @param lpMailer Mailer object used to send the lpMessage message containing the errors
  565. * @param lpMessage Failed message
  566. */
  567. HRESULT SendUndeliverable(ECSender *lpMailer, IMsgStore *lpStore,
  568. IMessage *lpMessage)
  569. {
  570. HRESULT hr = hrSuccess;
  571. object_ptr<IMAPIFolder> lpInbox;
  572. object_ptr<IMessage> lpErrorMsg;
  573. memory_ptr<ENTRYID> lpEntryID;
  574. ULONG cbEntryID;
  575. ULONG ulObjType;
  576. wstring newbody;
  577. memory_ptr<SPropValue> lpPropValue, lpPropValueAttach, lpPropArrayOriginal;
  578. unsigned int ulPropPos = 0;
  579. FILETIME ft;
  580. object_ptr<IAttach> lpAttach;
  581. object_ptr<IMessage> lpOriginalMessage;
  582. ULONG cValuesOriginal = 0;
  583. unsigned int ulPropModsPos;
  584. object_ptr<IMAPITable> lpTableMods;
  585. ULONG ulRows = 0;
  586. ULONG cEntries = 0;
  587. string strName, strType, strEmail;
  588. // CopyTo() var's
  589. unsigned int ulPropAttachPos;
  590. ULONG ulAttachNum;
  591. const std::vector<sFailedRecip> &temporaryFailedRecipients = lpMailer->getTemporaryFailedRecipients();
  592. const std::vector<sFailedRecip> &permanentFailedRecipients = lpMailer->getPermanentFailedRecipients();
  593. enum eORPos {
  594. OR_DISPLAY_TO, OR_DISPLAY_CC, OR_DISPLAY_BCC, OR_SEARCH_KEY, OR_SENDER_ADDRTYPE,
  595. OR_SENDER_EMAIL_ADDRESS, OR_SENDER_ENTRYID, OR_SENDER_NAME,
  596. OR_SENDER_SEARCH_KEY, OR_SENT_REPRESENTING_ADDRTYPE,
  597. OR_SENT_REPRESENTING_EMAIL_ADDRESS, OR_SENT_REPRESENTING_ENTRYID,
  598. OR_SENT_REPRESENTING_NAME, OR_SENT_REPRESENTING_SEARCH_KEY,
  599. OR_SUBJECT, OR_CLIENT_SUBMIT_TIME
  600. };
  601. // These props are on purpose without _A and _W
  602. static constexpr const SizedSPropTagArray(16, sPropsOriginal) = {
  603. 16,
  604. { PR_DISPLAY_TO, PR_DISPLAY_CC,
  605. PR_DISPLAY_BCC, PR_SEARCH_KEY,
  606. PR_SENDER_ADDRTYPE_W, PR_SENDER_EMAIL_ADDRESS,
  607. PR_SENDER_ENTRYID, PR_SENDER_NAME,
  608. PR_SENDER_SEARCH_KEY, PR_SENT_REPRESENTING_ADDRTYPE,
  609. PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ENTRYID,
  610. PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_SEARCH_KEY,
  611. PR_SUBJECT_W, PR_CLIENT_SUBMIT_TIME }
  612. };
  613. static constexpr const SizedSPropTagArray(7, sPropTagRecipient) = {
  614. 7,
  615. { PR_RECIPIENT_TYPE, PR_DISPLAY_NAME, PR_DISPLAY_TYPE,
  616. PR_ADDRTYPE, PR_EMAIL_ADDRESS,
  617. PR_ENTRYID, PR_SEARCH_KEY }
  618. };
  619. // open inbox
  620. hr = lpStore->GetReceiveFolder((LPTSTR)"IPM", 0, &cbEntryID, &~lpEntryID, NULL);
  621. if (hr != hrSuccess) {
  622. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to resolve incoming folder, error code: 0x%08X", hr);
  623. return hr;
  624. }
  625. hr = lpStore->OpenEntry(cbEntryID, lpEntryID, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpInbox);
  626. if (hr != hrSuccess || ulObjType != MAPI_FOLDER) {
  627. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to open inbox folder, error code: 0x%08X", hr);
  628. return MAPI_E_NOT_FOUND;
  629. }
  630. // make new message in inbox
  631. hr = lpInbox->CreateMessage(nullptr, 0, &~lpErrorMsg);
  632. if (hr != hrSuccess) {
  633. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to create undeliverable message, error code: 0x%08X", hr);
  634. return hr;
  635. }
  636. // Get properties from the original message
  637. hr = lpMessage->GetProps(sPropsOriginal, 0, &cValuesOriginal, &~lpPropArrayOriginal);
  638. if (FAILED(hr)) {
  639. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): GetPRops failed %x", hr);
  640. return hr;
  641. }
  642. hr = MAPIAllocateBuffer(sizeof(SPropValue) * 34, &~lpPropValue);
  643. if(hr != hrSuccess) {
  644. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): MAPIAllocateBuffers failed %x", hr);
  645. return hr;
  646. }
  647. // Subject
  648. lpPropValue[ulPropPos].ulPropTag = PR_SUBJECT_W;
  649. lpPropValue[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"Undelivered Mail Returned to Sender");
  650. // Message flags
  651. lpPropValue[ulPropPos].ulPropTag = PR_MESSAGE_FLAGS;
  652. lpPropValue[ulPropPos++].Value.ul = 0;
  653. // Message class
  654. lpPropValue[ulPropPos].ulPropTag = PR_MESSAGE_CLASS_W;
  655. lpPropValue[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"REPORT.IPM.Note.NDR");
  656. // Get the time to add to the message as PR_CLIENT_SUBMIT_TIME
  657. GetSystemTimeAsFileTime(&ft);
  658. // Submit time
  659. lpPropValue[ulPropPos].ulPropTag = PR_CLIENT_SUBMIT_TIME;
  660. lpPropValue[ulPropPos++].Value.ft = ft;
  661. // Delivery time
  662. lpPropValue[ulPropPos].ulPropTag = PR_MESSAGE_DELIVERY_TIME;
  663. lpPropValue[ulPropPos++].Value.ft = ft;
  664. lpPropValue[ulPropPos].ulPropTag = PR_SENDER_NAME_W;
  665. lpPropValue[ulPropPos++].Value.lpszW = (LPWSTR)L"Mail Delivery System";
  666. // Although lpszA is used, we just copy pointers. By not forcing _A or _W, this works in unicode and normal compile mode.
  667. // Set the properties PR_RCVD_REPRESENTING_* and PR_RECEIVED_BY_* and
  668. // PR_ORIGINAL_SENDER_* and PR_ORIGINAL_SENT_*
  669. if(PROP_TYPE(lpPropArrayOriginal[OR_SENDER_NAME].ulPropTag) != PT_ERROR) {
  670. lpPropValue[ulPropPos].ulPropTag = PR_RECEIVED_BY_NAME;
  671. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENDER_NAME].Value.lpszA;
  672. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENDER_NAME;
  673. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENDER_NAME].Value.lpszA;
  674. }
  675. if(PROP_TYPE(lpPropArrayOriginal[OR_SENDER_EMAIL_ADDRESS].ulPropTag) != PT_ERROR) {
  676. lpPropValue[ulPropPos].ulPropTag = PR_RECEIVED_BY_EMAIL_ADDRESS;
  677. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENDER_EMAIL_ADDRESS].Value.lpszA;
  678. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENDER_EMAIL_ADDRESS;
  679. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENDER_EMAIL_ADDRESS].Value.lpszA;
  680. }
  681. if(PROP_TYPE(lpPropArrayOriginal[OR_SENDER_ADDRTYPE].ulPropTag) != PT_ERROR) {
  682. lpPropValue[ulPropPos].ulPropTag = PR_RECEIVED_BY_ADDRTYPE;
  683. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENDER_ADDRTYPE].Value.lpszA;
  684. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENDER_ADDRTYPE;
  685. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENDER_ADDRTYPE].Value.lpszA;
  686. }
  687. if(PROP_TYPE(lpPropArrayOriginal[OR_SENDER_SEARCH_KEY].ulPropTag) != PT_ERROR) {
  688. lpPropValue[ulPropPos].ulPropTag = PR_RECEIVED_BY_SEARCH_KEY;
  689. lpPropValue[ulPropPos].Value.bin.cb = lpPropArrayOriginal[OR_SENDER_SEARCH_KEY].Value.bin.cb;
  690. lpPropValue[ulPropPos++].Value.bin.lpb = lpPropArrayOriginal[OR_SENDER_SEARCH_KEY].Value.bin.lpb;
  691. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENDER_SEARCH_KEY;
  692. lpPropValue[ulPropPos].Value.bin.cb = lpPropArrayOriginal[OR_SENDER_SEARCH_KEY].Value.bin.cb;
  693. lpPropValue[ulPropPos++].Value.bin.lpb = lpPropArrayOriginal[OR_SENDER_SEARCH_KEY].Value.bin.lpb;
  694. }
  695. if(PROP_TYPE(lpPropArrayOriginal[OR_SENDER_ENTRYID].ulPropTag) != PT_ERROR) {
  696. lpPropValue[ulPropPos].ulPropTag = PR_RECEIVED_BY_ENTRYID;
  697. lpPropValue[ulPropPos].Value.bin.cb = lpPropArrayOriginal[OR_SENDER_ENTRYID].Value.bin.cb;
  698. lpPropValue[ulPropPos++].Value.bin.lpb = lpPropArrayOriginal[OR_SENDER_ENTRYID].Value.bin.lpb;
  699. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENDER_ENTRYID;
  700. lpPropValue[ulPropPos].Value.bin.cb = lpPropArrayOriginal[OR_SENDER_ENTRYID].Value.bin.cb;
  701. lpPropValue[ulPropPos++].Value.bin.lpb = lpPropArrayOriginal[OR_SENDER_ENTRYID].Value.bin.lpb;
  702. }
  703. if(PROP_TYPE(lpPropArrayOriginal[OR_SENT_REPRESENTING_NAME].ulPropTag) != PT_ERROR) {
  704. lpPropValue[ulPropPos].ulPropTag = PR_RCVD_REPRESENTING_NAME;
  705. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENT_REPRESENTING_NAME].Value.lpszA;
  706. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENT_REPRESENTING_NAME;
  707. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENT_REPRESENTING_NAME].Value.lpszA;
  708. }
  709. if(PROP_TYPE(lpPropArrayOriginal[OR_SENT_REPRESENTING_EMAIL_ADDRESS].ulPropTag) != PT_ERROR) {
  710. lpPropValue[ulPropPos].ulPropTag = PR_RCVD_REPRESENTING_EMAIL_ADDRESS;
  711. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENT_REPRESENTING_EMAIL_ADDRESS].Value.lpszA;
  712. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS;
  713. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENT_REPRESENTING_EMAIL_ADDRESS].Value.lpszA;
  714. }
  715. if(PROP_TYPE(lpPropArrayOriginal[OR_SENT_REPRESENTING_ADDRTYPE].ulPropTag) != PT_ERROR) {
  716. lpPropValue[ulPropPos].ulPropTag = PR_RCVD_REPRESENTING_ADDRTYPE;
  717. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENT_REPRESENTING_ADDRTYPE].Value.lpszA;
  718. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE;
  719. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SENT_REPRESENTING_ADDRTYPE].Value.lpszA;
  720. }
  721. if(PROP_TYPE(lpPropArrayOriginal[OR_SENT_REPRESENTING_SEARCH_KEY].ulPropTag) != PT_ERROR) {
  722. lpPropValue[ulPropPos].ulPropTag = PR_RCVD_REPRESENTING_SEARCH_KEY;
  723. lpPropValue[ulPropPos].Value.bin.cb = lpPropArrayOriginal[OR_SENT_REPRESENTING_SEARCH_KEY].Value.bin.cb;
  724. lpPropValue[ulPropPos++].Value.bin.lpb = lpPropArrayOriginal[OR_SENT_REPRESENTING_SEARCH_KEY].Value.bin.lpb;
  725. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY;
  726. lpPropValue[ulPropPos].Value.bin.cb = lpPropArrayOriginal[OR_SENT_REPRESENTING_SEARCH_KEY].Value.bin.cb;
  727. lpPropValue[ulPropPos++].Value.bin.lpb = lpPropArrayOriginal[OR_SENT_REPRESENTING_SEARCH_KEY].Value.bin.lpb;
  728. }
  729. if(PROP_TYPE(lpPropArrayOriginal[OR_SENT_REPRESENTING_ENTRYID].ulPropTag) != PT_ERROR) {
  730. lpPropValue[ulPropPos].ulPropTag = PR_RCVD_REPRESENTING_ENTRYID;
  731. lpPropValue[ulPropPos].Value.bin.cb = lpPropArrayOriginal[OR_SENT_REPRESENTING_ENTRYID].Value.bin.cb;
  732. lpPropValue[ulPropPos++].Value.bin.lpb = lpPropArrayOriginal[OR_SENT_REPRESENTING_ENTRYID].Value.bin.lpb;
  733. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SENT_REPRESENTING_ENTRYID;
  734. lpPropValue[ulPropPos].Value.bin.cb = lpPropArrayOriginal[OR_SENT_REPRESENTING_ENTRYID].Value.bin.cb;
  735. lpPropValue[ulPropPos++].Value.bin.lpb = lpPropArrayOriginal[OR_SENT_REPRESENTING_ENTRYID].Value.bin.lpb;
  736. }
  737. // Original display to
  738. if(PROP_TYPE(lpPropArrayOriginal[OR_DISPLAY_TO].ulPropTag) != PT_ERROR) {
  739. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_DISPLAY_TO;
  740. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_DISPLAY_TO].Value.lpszA;
  741. }
  742. // Original display cc
  743. if(PROP_TYPE(lpPropArrayOriginal[OR_DISPLAY_CC].ulPropTag) != PT_ERROR) {
  744. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_DISPLAY_CC;
  745. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_DISPLAY_CC].Value.lpszA;
  746. }
  747. // Original display bcc
  748. if(PROP_TYPE(lpPropArrayOriginal[OR_DISPLAY_BCC].ulPropTag) != PT_ERROR) {
  749. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_DISPLAY_BCC;
  750. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_DISPLAY_BCC].Value.lpszA;
  751. }
  752. // Original subject
  753. if(PROP_TYPE(lpPropArrayOriginal[OR_SUBJECT].ulPropTag) != PT_ERROR) {
  754. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SUBJECT;
  755. lpPropValue[ulPropPos++].Value.lpszA = lpPropArrayOriginal[OR_SUBJECT].Value.lpszA;
  756. }
  757. // Original submit time
  758. if(PROP_TYPE(lpPropArrayOriginal[OR_CLIENT_SUBMIT_TIME].ulPropTag) != PT_ERROR) {
  759. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SUBMIT_TIME;
  760. lpPropValue[ulPropPos++].Value.ft = lpPropArrayOriginal[OR_CLIENT_SUBMIT_TIME].Value.ft;
  761. }
  762. // Original searchkey
  763. if(PROP_TYPE(lpPropArrayOriginal[OR_SEARCH_KEY].ulPropTag) != PT_ERROR) {
  764. lpPropValue[ulPropPos].ulPropTag = PR_ORIGINAL_SEARCH_KEY;
  765. lpPropValue[ulPropPos].Value.bin.cb = lpPropArrayOriginal[OR_SEARCH_KEY].Value.bin.cb;
  766. lpPropValue[ulPropPos++].Value.bin.lpb = lpPropArrayOriginal[OR_SEARCH_KEY].Value.bin.lpb;
  767. }
  768. // Add the original message into the errorMessage
  769. hr = lpErrorMsg->CreateAttach(nullptr, 0, &ulAttachNum, &~lpAttach);
  770. if (hr != hrSuccess) {
  771. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to create attachment, error code: 0x%08X", hr);
  772. return hr;
  773. }
  774. hr = lpAttach->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY, &~lpOriginalMessage);
  775. if (hr != hrSuccess) {
  776. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): OpenProperty failed %x", hr);
  777. return hr;
  778. }
  779. hr = lpMessage->CopyTo(0, NULL, NULL, 0, NULL, &IID_IMessage, (LPVOID)lpOriginalMessage, 0, NULL);
  780. if (hr != hrSuccess) {
  781. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): CopyTo failed %x", hr);
  782. return hr;
  783. }
  784. // Remove MAPI_P1 recipients. These are present when you resend a resent message. They shouldn't be there since
  785. // we should be resending the original message
  786. hr = RemoveP1Recipients(lpOriginalMessage);
  787. if (hr != hrSuccess) {
  788. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): RemoveP1Recipients failed %x", hr);
  789. return hr;
  790. }
  791. hr = lpOriginalMessage->SaveChanges(KEEP_OPEN_READWRITE);
  792. if (hr != hrSuccess) {
  793. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): SaveChanges failed %x", hr);
  794. return hr;
  795. }
  796. ulPropAttachPos = 0;
  797. hr = MAPIAllocateBuffer(sizeof(SPropValue) * 4, &~lpPropValueAttach);
  798. if (hr != hrSuccess)
  799. return hr;
  800. lpPropValueAttach[ulPropAttachPos].ulPropTag = PR_ATTACH_METHOD;
  801. lpPropValueAttach[ulPropAttachPos++].Value.ul = ATTACH_EMBEDDED_MSG;
  802. lpPropValueAttach[ulPropAttachPos].ulPropTag = PR_ATTACH_MIME_TAG_W;
  803. lpPropValueAttach[ulPropAttachPos++].Value.lpszW = const_cast<wchar_t *>(L"message/rfc822");
  804. if(PROP_TYPE(lpPropArrayOriginal[OR_SUBJECT].ulPropTag) != PT_ERROR) {
  805. lpPropValueAttach[ulPropAttachPos].ulPropTag = CHANGE_PROP_TYPE(PR_DISPLAY_NAME, PROP_TYPE(lpPropArrayOriginal[OR_SUBJECT].ulPropTag));
  806. lpPropValueAttach[ulPropAttachPos++].Value.lpszA = lpPropArrayOriginal[OR_SUBJECT].Value.lpszA;
  807. }
  808. lpPropValueAttach[ulPropAttachPos].ulPropTag = PR_RENDERING_POSITION;
  809. lpPropValueAttach[ulPropAttachPos++].Value.ul = -1;
  810. hr = lpAttach->SetProps(ulPropAttachPos, lpPropValueAttach, NULL);
  811. if (hr != hrSuccess) {
  812. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): SetProps failed %x", hr);
  813. return hr;
  814. }
  815. hr = lpAttach->SaveChanges(KEEP_OPEN_READWRITE);
  816. if (hr != hrSuccess) {
  817. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): SaveChanges failed %x", hr);
  818. return hr;
  819. }
  820. // add failed recipients to error report
  821. hr = lpMessage->GetRecipientTable(MAPI_UNICODE, &~lpTableMods);
  822. if (hr != hrSuccess) {
  823. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): GetRecipientTable failed %x", hr);
  824. return hr;
  825. }
  826. hr = lpTableMods->SetColumns(sPropTagRecipient, TBL_BATCH);
  827. if (hr != hrSuccess) {
  828. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): SetColumns failed %x", hr);
  829. return hr;
  830. }
  831. hr = lpTableMods->GetRowCount(0, &ulRows);
  832. if (hr != hrSuccess) {
  833. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): GetRowCount failed %x", hr);
  834. return hr;
  835. }
  836. if (ulRows == 0 || (permanentFailedRecipients.empty() && temporaryFailedRecipients.empty())) {
  837. // No specific failed recipients, so the entire message failed
  838. // If there's a pr_body, outlook will display that, and not the 'default' outlook error report
  839. // Message error
  840. newbody = L"Unfortunately, I was unable to deliver your mail.\nThe error given was:\n\n";
  841. newbody.append(lpMailer->getErrorString());
  842. newbody.append(L"\n\nYou may need to contact your e-mail administrator to solve this problem.\n");
  843. lpPropValue[ulPropPos].ulPropTag = PR_BODY_W;
  844. lpPropValue[ulPropPos++].Value.lpszW = (WCHAR*)newbody.c_str();
  845. if (ulRows > 0) {
  846. // All recipients failed, therefore all recipient need to be in the MDN recipient table
  847. rowset_ptr lpRows;
  848. hr = lpTableMods->QueryRows(-1, 0, &~lpRows);
  849. if (hr != hrSuccess) {
  850. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): QueryRows failed %x", hr);
  851. return hr;
  852. }
  853. hr = lpErrorMsg->ModifyRecipients(MODRECIP_ADD, reinterpret_cast<ADRLIST *>(lpRows.get()));
  854. if (hr != hrSuccess) {
  855. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): ModifyRecipients failed %x", hr);
  856. return hr;
  857. }
  858. }
  859. }
  860. else if (ulRows > 0)
  861. {
  862. convert_context converter;
  863. newbody = L"Unfortunately, I was unable to deliver your mail to the/some of the recipient(s).\n";
  864. newbody.append(L"You may need to contact your e-mail administrator to solve this problem.\n");
  865. if (!temporaryFailedRecipients.empty()) {
  866. newbody.append(L"\nRecipients that will be retried:\n");
  867. for (size_t i = 0; i < temporaryFailedRecipients.size(); ++i) {
  868. const sFailedRecip &cur = temporaryFailedRecipients.at(i);
  869. newbody.append(L"\t");
  870. newbody.append(cur.strRecipName.c_str());
  871. newbody.append(L" <");
  872. newbody.append(converter.convert_to<wchar_t *>(cur.strRecipEmail));
  873. newbody.append(L">\n");
  874. }
  875. }
  876. if (!permanentFailedRecipients.empty()) {
  877. newbody.append(L"\nRecipients that failed permanently:\n");
  878. for (size_t i = 0; i < permanentFailedRecipients.size(); ++i) {
  879. const sFailedRecip &cur = permanentFailedRecipients.at(i);
  880. newbody.append(L"\t");
  881. newbody.append(cur.strRecipName.c_str());
  882. newbody.append(L" <");
  883. newbody.append(converter.convert_to<wchar_t *>(cur.strRecipEmail));
  884. newbody.append(L">\n");
  885. }
  886. }
  887. lpPropValue[ulPropPos].ulPropTag = PR_BODY_W;
  888. lpPropValue[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(newbody.c_str());
  889. // Only some recipients failed, so add only failed recipients to the MDN message. This causes
  890. // resends only to go to those recipients. This means we should add all error recipients to the
  891. // recipient list of the MDN message.
  892. adrlist_ptr lpMods;
  893. hr = MAPIAllocateBuffer(CbNewADRLIST(temporaryFailedRecipients.size()), &~lpMods);
  894. if (hr != hrSuccess)
  895. return hr;
  896. lpMods->cEntries = 0;
  897. for (size_t j = 0; j < temporaryFailedRecipients.size(); ++j) {
  898. const sFailedRecip &cur = temporaryFailedRecipients.at(j);
  899. if ((hr = MAPIAllocateBuffer(sizeof(SPropValue) * 10, (void**)&lpMods->aEntries[cEntries].rgPropVals)) != hrSuccess)
  900. return hr;
  901. ulPropModsPos = 0;
  902. lpMods->cEntries = cEntries;
  903. auto &pv = lpMods->aEntries[cEntries].rgPropVals;
  904. pv[ulPropModsPos].ulPropTag = PR_RECIPIENT_TYPE;
  905. pv[ulPropModsPos++].Value.ul = MAPI_TO;
  906. pv[ulPropModsPos].ulPropTag = PR_EMAIL_ADDRESS_A;
  907. pv[ulPropModsPos++].Value.lpszA = const_cast<char *>(cur.strRecipEmail.c_str());
  908. pv[ulPropModsPos].ulPropTag = PR_ADDRTYPE_W;
  909. pv[ulPropModsPos++].Value.lpszW = const_cast<wchar_t *>(L"SMTP");
  910. pv[ulPropModsPos].ulPropTag = PR_DISPLAY_NAME_W;
  911. if (!cur.strRecipName.empty())
  912. pv[ulPropModsPos++].Value.lpszW = const_cast<wchar_t *>(cur.strRecipName.c_str());
  913. else
  914. pv[ulPropModsPos++].Value.lpszW = converter.convert_to<wchar_t *>(cur.strRecipEmail);
  915. pv[ulPropModsPos].ulPropTag = PR_REPORT_TEXT_A;
  916. pv[ulPropModsPos++].Value.lpszA = const_cast<char *>(cur.strSMTPResponse.c_str());
  917. pv[ulPropModsPos].ulPropTag = PR_REPORT_TIME;
  918. pv[ulPropModsPos++].Value.ft = ft;
  919. pv[ulPropModsPos].ulPropTag = PR_TRANSMITABLE_DISPLAY_NAME_A;
  920. pv[ulPropModsPos++].Value.lpszA = const_cast<char *>(cur.strRecipEmail.c_str());
  921. pv[ulPropModsPos].ulPropTag = 0x0C200003; // PR_NDR_STATUS_CODE;
  922. pv[ulPropModsPos++].Value.ul = cur.ulSMTPcode;
  923. pv[ulPropModsPos].ulPropTag = PR_NDR_DIAG_CODE;
  924. pv[ulPropModsPos++].Value.ul = MAPI_DIAG_MAIL_RECIPIENT_UNKNOWN;
  925. pv[ulPropModsPos].ulPropTag = PR_NDR_REASON_CODE;
  926. pv[ulPropModsPos++].Value.ul = MAPI_REASON_TRANSFER_FAILED;
  927. lpMods->aEntries[cEntries].cValues = ulPropModsPos;
  928. ++cEntries;
  929. }
  930. lpMods->cEntries = cEntries;
  931. hr = lpErrorMsg->ModifyRecipients(MODRECIP_ADD, lpMods);
  932. if (hr != hrSuccess)
  933. return hr;
  934. }
  935. // Add properties
  936. hr = lpErrorMsg->SetProps(ulPropPos, lpPropValue, NULL);
  937. if (hr != hrSuccess) {
  938. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendUndeliverable(): SetProps failed %x", hr);
  939. return hr;
  940. }
  941. // save message
  942. hr = lpErrorMsg->SaveChanges(KEEP_OPEN_READONLY);
  943. if (hr != hrSuccess) {
  944. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to commit message: 0x%08X", hr);
  945. return hr;
  946. }
  947. // New mail notification
  948. if (HrNewMailNotification(lpStore, lpErrorMsg) != hrSuccess)
  949. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to send 'New Mail' notification, error code: 0x%08X", hr);
  950. return hrSuccess;
  951. }
  952. /**
  953. * Converts a Contacts folder EntryID to a ZARAFA addressbook EntryID.
  954. *
  955. * A contacts folder EntryID contains an offset that is an index in three different possible EntryID named properties.
  956. *
  957. * @param[in] lpUserStore The store of the user where the contact is stored.
  958. * @param[in] cbEntryId The number of bytes in lpEntryId
  959. * @param[in] lpEntryId The contact EntryID
  960. * @param[in] eid_size The number of bytes in eidp
  961. * @param[in] eidp The EntryID where the contact points to
  962. * @return HRESULT
  963. */
  964. static HRESULT ContactToKopano(IMsgStore *lpUserStore,
  965. ULONG cbEntryId, const ENTRYID *lpEntryId, ULONG *eid_size,
  966. LPENTRYID *eidp)
  967. {
  968. HRESULT hr = hrSuccess;
  969. auto lpContabEntryID = reinterpret_cast<const CONTAB_ENTRYID *>(lpEntryId);
  970. auto guid = reinterpret_cast<const GUID *>(&lpContabEntryID->muid);
  971. ULONG ulObjType;
  972. object_ptr<IMailUser> lpContact;
  973. ULONG cValues;
  974. LPSPropValue lpEntryIds = NULL;
  975. memory_ptr<SPropTagArray> lpPropTags;
  976. memory_ptr<MAPINAMEID> lpNames;
  977. memory_ptr<MAPINAMEID *> lppNames;
  978. if (sizeof(CONTAB_ENTRYID) > cbEntryId ||
  979. *guid != PSETID_CONTACT_FOLDER_RECIPIENT ||
  980. lpContabEntryID->email_offset > 2)
  981. return MAPI_E_NOT_FOUND;
  982. hr = lpUserStore->OpenEntry(lpContabEntryID->cbeid, reinterpret_cast<ENTRYID *>(const_cast<BYTE *>(lpContabEntryID->abeid)), nullptr, 0, &ulObjType, &~lpContact);
  983. if (hr != hrSuccess) {
  984. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open contact entryid: %s (%x)",
  985. GetMAPIErrorMessage(hr), hr);
  986. return hr;
  987. }
  988. hr = MAPIAllocateBuffer(sizeof(MAPINAMEID) * 3, &~lpNames);
  989. if (hr != hrSuccess) {
  990. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "No memory for named ids from contact: %s (%x)",
  991. GetMAPIErrorMessage(hr), hr);
  992. return hr;
  993. }
  994. hr = MAPIAllocateBuffer(sizeof(LPMAPINAMEID) * 3, &~lppNames);
  995. if (hr != hrSuccess) {
  996. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "No memory for named ids from contact: %s (%x)",
  997. GetMAPIErrorMessage(hr), hr);
  998. return hr;
  999. }
  1000. // Email1EntryID
  1001. lpNames[0].lpguid = (GUID*)&PSETID_Address;
  1002. lpNames[0].ulKind = MNID_ID;
  1003. lpNames[0].Kind.lID = 0x8085;
  1004. lppNames[0] = &lpNames[0];
  1005. // Email2EntryID
  1006. lpNames[1].lpguid = (GUID*)&PSETID_Address;
  1007. lpNames[1].ulKind = MNID_ID;
  1008. lpNames[1].Kind.lID = 0x8095;
  1009. lppNames[1] = &lpNames[1];
  1010. // Email3EntryID
  1011. lpNames[2].lpguid = (GUID*)&PSETID_Address;
  1012. lpNames[2].ulKind = MNID_ID;
  1013. lpNames[2].Kind.lID = 0x80A5;
  1014. lppNames[2] = &lpNames[2];
  1015. hr = lpContact->GetIDsFromNames(3, lppNames, 0, &~lpPropTags);
  1016. if (hr != hrSuccess) {
  1017. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Error while retrieving named data from contact: %s (%x)",
  1018. GetMAPIErrorMessage(hr), hr);
  1019. return hr;
  1020. }
  1021. hr = lpContact->GetProps(lpPropTags, 0, &cValues, &lpEntryIds);
  1022. if (FAILED(hr)) {
  1023. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get named properties: %s (%x)",
  1024. GetMAPIErrorMessage(hr), hr);
  1025. return hr;
  1026. }
  1027. if (PROP_TYPE(lpEntryIds[lpContabEntryID->email_offset].ulPropTag) != PT_BINARY) {
  1028. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Offset %d not found in contact", lpContabEntryID->email_offset);
  1029. return MAPI_E_NOT_FOUND;
  1030. }
  1031. hr = MAPIAllocateBuffer(lpEntryIds[lpContabEntryID->email_offset].Value.bin.cb, (void**)eidp);
  1032. if (hr != hrSuccess) {
  1033. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "No memory for contact eid: %s (%x)",
  1034. GetMAPIErrorMessage(hr), hr);
  1035. return hr;
  1036. }
  1037. memcpy(*eidp, lpEntryIds[lpContabEntryID->email_offset].Value.bin.lpb, lpEntryIds[lpContabEntryID->email_offset].Value.bin.cb);
  1038. *eid_size = lpEntryIds[lpContabEntryID->email_offset].Value.bin.cb;
  1039. return hrSuccess;
  1040. }
  1041. /**
  1042. * Converts an One-off EntryID to a ZARAFA addressbook EntryID.
  1043. *
  1044. * @param[in] lpAddrBook The Global Addressbook of the user sending the mail.
  1045. * @param[in] ulSMTPEID The number of bytes in lpSMTPEID
  1046. * @param[in] lpSMTPEID The One off EntryID.
  1047. * @param[out] eid_size The number of bytes in eidp
  1048. * @param[out] eidp The ZARAFA entryid of the user defined in the One off.
  1049. * @return HRESULT
  1050. * @retval MAPI_E_NOT_FOUND User not a Kopano user, or lpSMTPEID is not an One-off EntryID
  1051. */
  1052. static HRESULT SMTPToZarafa(LPADRBOOK lpAddrBook, ULONG ulSMTPEID,
  1053. const ENTRYID *lpSMTPEID, ULONG *eid_size, LPENTRYID *eidp)
  1054. {
  1055. HRESULT hr = hrSuccess;
  1056. wstring wstrName, wstrType, wstrEmailAddress;
  1057. adrlist_ptr lpAList;
  1058. const SPropValue *lpSpoofEID;
  1059. LPENTRYID lpSpoofBin = NULL;
  1060. // representing entryid can also be a one off id, so search the user, and then get the entryid again ..
  1061. // we then always should have yourself as the sender, otherwise: denied
  1062. if (ECParseOneOff(lpSMTPEID, ulSMTPEID, wstrName, wstrType, wstrEmailAddress) != hrSuccess)
  1063. return MAPI_E_NOT_FOUND;
  1064. hr = MAPIAllocateBuffer(CbNewADRLIST(1), &~lpAList);
  1065. if (hr != hrSuccess)
  1066. return hrSuccess;
  1067. lpAList->cEntries = 1;
  1068. lpAList->aEntries[0].cValues = 1;
  1069. if ((hr = MAPIAllocateBuffer(sizeof(SPropValue) * lpAList->aEntries[0].cValues, (void**)&lpAList->aEntries[0].rgPropVals)) != hrSuccess)
  1070. return hrSuccess;
  1071. lpAList->aEntries[0].rgPropVals[0].ulPropTag = PR_DISPLAY_NAME_W;
  1072. lpAList->aEntries[0].rgPropVals[0].Value.lpszW = (WCHAR*)wstrEmailAddress.c_str();
  1073. hr = lpAddrBook->ResolveName(0, EMS_AB_ADDRESS_LOOKUP, NULL, lpAList);
  1074. if (hr != hrSuccess) {
  1075. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SMTPToZarafa(): ResolveName failed %x", hr);
  1076. return hrSuccess;
  1077. }
  1078. lpSpoofEID = PCpropFindProp(lpAList->aEntries[0].rgPropVals, lpAList->aEntries[0].cValues, PR_ENTRYID);
  1079. if (!lpSpoofEID) {
  1080. hr = MAPI_E_NOT_FOUND;
  1081. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SMTPToZarafa(): PpropFindProp failed %x", hr);
  1082. return hrSuccess;
  1083. }
  1084. hr = MAPIAllocateBuffer(lpSpoofEID->Value.bin.cb, (void**)&lpSpoofBin);
  1085. if (hr != hrSuccess) {
  1086. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SMTPToZarafa(): MAPIAllocateBuffer failed %x", hr);
  1087. return hr;
  1088. }
  1089. memcpy(lpSpoofBin, lpSpoofEID->Value.bin.lpb, lpSpoofEID->Value.bin.cb);
  1090. *eidp = lpSpoofBin;
  1091. *eid_size = lpSpoofEID->Value.bin.cb;
  1092. return hrSuccess;
  1093. }
  1094. /**
  1095. * Find a user in a group. Used when checking for send-as users.
  1096. *
  1097. * @param[in] lpAdrBook The Global Addressbook of the user sending the mail.
  1098. * @param[in] ulOwnerCB Number of bytes in lpOwnerEID
  1099. * @param[in] lpOwnerEID The EntryID of the user to find in the group
  1100. * @param[in] ulDistListCB The number of bytes in lpDistlistEID
  1101. * @param[in] lpDistlistEID The EntryID of the group
  1102. * @param[out] lpulCmp The result of the comparison of CompareEntryID. FALSE if not found, TRUE if found.
  1103. * @param[in] level Internal parameter to keep track of recursion. Max is 10 levels deep before it gives up.
  1104. * @return HRESULT
  1105. */
  1106. static HRESULT HrFindUserInGroup(LPADRBOOK lpAdrBook, ULONG ulOwnerCB,
  1107. LPENTRYID lpOwnerEID, ULONG ulDistListCB, LPENTRYID lpDistListEID,
  1108. ULONG *lpulCmp, int level = 0)
  1109. {
  1110. HRESULT hr = hrSuccess;
  1111. ULONG ulCmp = 0;
  1112. ULONG ulObjType = 0;
  1113. object_ptr<IDistList> lpDistList;
  1114. object_ptr<IMAPITable> lpMembersTable;
  1115. static constexpr const SizedSPropTagArray(2, sptaIDProps) =
  1116. {2, {PR_ENTRYID, PR_OBJECT_TYPE}};
  1117. if (lpulCmp == nullptr)
  1118. return MAPI_E_INVALID_PARAMETER;
  1119. if (level > 10) {
  1120. hr = MAPI_E_TOO_COMPLEX;
  1121. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrFindUserInGroup(): level too big %d: %s (%x)",
  1122. level, GetMAPIErrorMessage(hr), hr);
  1123. return hr;
  1124. }
  1125. hr = lpAdrBook->OpenEntry(ulDistListCB, lpDistListEID, nullptr, 0, &ulObjType, &~lpDistList);
  1126. if (hr != hrSuccess) {
  1127. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrFindUserInGroup(): OpenEntry failed: %s (%x)",
  1128. GetMAPIErrorMessage(hr), hr);
  1129. return hr;
  1130. }
  1131. hr = lpDistList->GetContentsTable(0, &~lpMembersTable);
  1132. if (hr != hrSuccess) {
  1133. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrFindUserInGroup(): GetContentsTable failed: %s (%x)",
  1134. GetMAPIErrorMessage(hr), hr);
  1135. return hr;
  1136. }
  1137. hr = lpMembersTable->SetColumns(sptaIDProps, 0);
  1138. if (hr != hrSuccess) {
  1139. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrFindUserInGroup(): SetColumns failed: %s (%x)",
  1140. GetMAPIErrorMessage(hr), hr);
  1141. return hr;
  1142. }
  1143. // sort on PR_OBJECT_TYPE (MAILUSER < DISTLIST) ?
  1144. while (TRUE) {
  1145. rowset_ptr lpRowSet;
  1146. hr = lpMembersTable->QueryRows(1, 0, &~lpRowSet);
  1147. if (hr != hrSuccess) {
  1148. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrFindUserInGroup(): QueryRows failed: %s (%x)",
  1149. GetMAPIErrorMessage(hr), hr);
  1150. return hr;
  1151. }
  1152. if (lpRowSet->cRows == 0)
  1153. break;
  1154. if (lpRowSet->aRow[0].lpProps[0].ulPropTag != PR_ENTRYID || lpRowSet->aRow[0].lpProps[1].ulPropTag != PR_OBJECT_TYPE)
  1155. continue;
  1156. if (lpRowSet->aRow[0].lpProps[1].Value.ul == MAPI_MAILUSER)
  1157. hr = lpAdrBook->CompareEntryIDs(ulOwnerCB, lpOwnerEID,
  1158. lpRowSet->aRow[0].lpProps[0].Value.bin.cb, (LPENTRYID)lpRowSet->aRow[0].lpProps[0].Value.bin.lpb,
  1159. 0, &ulCmp);
  1160. else if (lpRowSet->aRow[0].lpProps[1].Value.ul == MAPI_DISTLIST)
  1161. hr = HrFindUserInGroup(lpAdrBook, ulOwnerCB, lpOwnerEID,
  1162. lpRowSet->aRow[0].lpProps[0].Value.bin.cb, (LPENTRYID)lpRowSet->aRow[0].lpProps[0].Value.bin.lpb,
  1163. &ulCmp, level+1);
  1164. if (hr == hrSuccess && ulCmp == TRUE)
  1165. break;
  1166. }
  1167. *lpulCmp = ulCmp;
  1168. return hrSuccess;
  1169. }
  1170. /**
  1171. * Looks up a user in the addressbook, and opens the store of that user.
  1172. *
  1173. * @param[in] lpAddrBook The Global Addressbook of the user
  1174. * @param[in] lpUserStore The store of the user, just to create the deletegate store entry id
  1175. * @param[in] lpAdminSession We need full rights on the delegate store, so use the admin session to open it
  1176. * @param[in] ulRepresentCB Number of bytes in lpRepresentEID
  1177. * @param[in] lpRepresentEID EntryID of the delegate user
  1178. * @param[out] lppRepStore The store of the delegate
  1179. * @return HRESULT
  1180. */
  1181. static HRESULT HrOpenRepresentStore(IAddrBook *lpAddrBook,
  1182. IMsgStore *lpUserStore, IMAPISession *lpAdminSession, ULONG ulRepresentCB,
  1183. LPENTRYID lpRepresentEID, LPMDB *lppRepStore)
  1184. {
  1185. HRESULT hr = hrSuccess;
  1186. ULONG ulObjType = 0;
  1187. object_ptr<IMailUser> lpRepresenting;
  1188. memory_ptr<SPropValue> lpRepAccount;
  1189. object_ptr<IExchangeManageStore> lpExchangeManageStore;
  1190. ULONG ulRepStoreCB = 0;
  1191. memory_ptr<ENTRYID> lpRepStoreEID;
  1192. object_ptr<IMsgStore> lpRepStore;
  1193. hr = lpAddrBook->OpenEntry(ulRepresentCB, lpRepresentEID, nullptr, 0, &ulObjType, &~lpRepresenting);
  1194. if (hr != hrSuccess) {
  1195. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Unable to open representing user in addressbook: %s (%x)",
  1196. GetMAPIErrorMessage(hr), hr);
  1197. return MAPI_E_NOT_FOUND;
  1198. }
  1199. hr = HrGetOneProp(lpRepresenting, PR_ACCOUNT, &~lpRepAccount);
  1200. if (hr != hrSuccess) {
  1201. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Unable to find account name for representing user: %s (%x)",
  1202. GetMAPIErrorMessage(hr), hr);
  1203. return MAPI_E_NOT_FOUND;
  1204. }
  1205. hr = lpUserStore->QueryInterface(IID_IExchangeManageStore, &~lpExchangeManageStore);
  1206. if (hr != hrSuccess) {
  1207. g_lpLogger->Log(EC_LOGLEVEL_INFO, "IExchangeManageStore interface not found: %s (%x)",
  1208. GetMAPIErrorMessage(hr), hr);
  1209. return hr;
  1210. }
  1211. hr = lpExchangeManageStore->CreateStoreEntryID(NULL, lpRepAccount->Value.LPSZ, fMapiUnicode, &ulRepStoreCB, &~lpRepStoreEID);
  1212. if (hr != hrSuccess) {
  1213. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to create store entryid for representing user '" TSTRING_PRINTF "': %s (%x)",
  1214. lpRepAccount->Value.LPSZ, GetMAPIErrorMessage(hr), hr);
  1215. return hr;
  1216. }
  1217. // Use the admin session to open the store, so we have full rights
  1218. hr = lpAdminSession->OpenMsgStore(0, ulRepStoreCB, lpRepStoreEID, nullptr, MAPI_BEST_ACCESS, &~lpRepStore);
  1219. if (hr != hrSuccess) {
  1220. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open store of representing user '" TSTRING_PRINTF "': %s (%x)",
  1221. lpRepAccount->Value.LPSZ, GetMAPIErrorMessage(hr), hr);
  1222. return hr;
  1223. }
  1224. return lpRepStore->QueryInterface(IID_IMsgStore,
  1225. reinterpret_cast<void **>(lppRepStore));
  1226. }
  1227. /**
  1228. * Checks for the presence of an Addressbook EntryID in a given
  1229. * array. If the array contains a group EntryID, it is opened, and
  1230. * searched within the group for the presence of the given EntryID.
  1231. *
  1232. * @param[in] szFunc Context name how this function is used. Used in logging.
  1233. * @param[in] lpszMailer The name of the user sending the email.
  1234. * @param[in] lpAddrBook The Global Addressbook.
  1235. * @param[in] ulOwnerCB number of bytes in lpOwnerEID
  1236. * @param[in] lpOwnerEID EntryID of the "Owner" object, which is searched in the array
  1237. * @param[in] cValues Number of EntryIDs in lpEntryIds
  1238. * @param[in] lpEntryIDs Array of EntryIDs to search in
  1239. * @param[out] lpulObjType lpOwnerEID was found in this type of object (user or group)
  1240. * @param[out] lpbAllowed User is (not) found in array
  1241. *
  1242. * @return hrSuccess
  1243. */
  1244. static HRESULT HrCheckAllowedEntryIDArray(const char *szFunc,
  1245. const wchar_t *lpszMailer, IAddrBook *lpAddrBook, ULONG ulOwnerCB,
  1246. LPENTRYID lpOwnerEID, ULONG cValues, SBinary *lpEntryIDs,
  1247. ULONG *lpulObjType, bool *lpbAllowed)
  1248. {
  1249. HRESULT hr = hrSuccess;
  1250. ULONG ulObjType;
  1251. ULONG ulCmpRes;
  1252. for (ULONG i = 0; i < cValues; ++i) {
  1253. // quick way to see what object the entryid points to .. otherwise we need to call OpenEntry, which is slow
  1254. if (GetNonPortableObjectType(lpEntryIDs[i].cb, (LPENTRYID)lpEntryIDs[i].lpb, &ulObjType))
  1255. continue;
  1256. if (ulObjType == MAPI_DISTLIST) {
  1257. hr = HrFindUserInGroup(lpAddrBook, ulOwnerCB, lpOwnerEID, lpEntryIDs[i].cb, (LPENTRYID)lpEntryIDs[i].lpb, &ulCmpRes);
  1258. } else if (ulObjType == MAPI_MAILUSER) {
  1259. hr = lpAddrBook->CompareEntryIDs(ulOwnerCB, lpOwnerEID, lpEntryIDs[i].cb, (LPENTRYID)lpEntryIDs[i].lpb, 0, &ulCmpRes);
  1260. } else {
  1261. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Invalid object %d in %s list of user '%ls': %s (%x)",
  1262. ulObjType, szFunc, lpszMailer, GetMAPIErrorMessage(hr), hr);
  1263. continue;
  1264. }
  1265. if (hr == hrSuccess && ulCmpRes == TRUE) {
  1266. *lpulObjType = ulObjType;
  1267. *lpbAllowed = true;
  1268. // always return success, since lpbAllowed is always written
  1269. return hrSuccess;
  1270. }
  1271. }
  1272. *lpbAllowed = false;
  1273. return hrSuccess;
  1274. }
  1275. /**
  1276. * Checks if the current user is has send-as rights as specified user. Needs
  1277. * admin rights to open the delegate store.
  1278. *
  1279. * @param[in] lpAddrBook The Global Addressbook of the user trying to send an email.
  1280. * @param[in] lpUserStore The store of the user trying to send an email.
  1281. * @param[in] lpAdminSession MAPI session of the Kopano SYSTEM user.
  1282. * @param[in] lpMailer ECSender object (inetmapi), used to set an error for an error mail if not allowed.
  1283. * @param[in] ulOwnerCB Number of bytes in lpOwnerEID
  1284. * @param[in] lpOwnerEID EntryID of the user sending the mail.
  1285. * @param[in] ulRepresentCB Number of bytes in lpRepresentEID.
  1286. * @param[in] lpRepresentEID EntryID of the user set in the From address. Can be a One-off entryid.
  1287. * @param[out] lpbAllowed Set to true if the lpOwnerEID is a delegate of lpRepresentEID
  1288. * @param[out] lppRepStore The store of the delegate when allowed.
  1289. * @return HRESULT
  1290. */
  1291. static HRESULT CheckSendAs(IAddrBook *lpAddrBook, IMsgStore *lpUserStore,
  1292. IMAPISession *lpAdminSession, ECSender *lpMailer, ULONG ulOwnerCB,
  1293. LPENTRYID lpOwnerEID, ULONG ulRepresentCB, LPENTRYID lpRepresentEID,
  1294. bool *lpbAllowed, LPMDB *lppRepStore)
  1295. {
  1296. HRESULT hr = hrSuccess;
  1297. bool bAllowed = false;
  1298. bool bHasStore = false;
  1299. ULONG ulObjType;
  1300. object_ptr<IMailUser> lpMailboxOwner, lpRepresenting;
  1301. memory_ptr<SPropValue> lpOwnerProps, lpRepresentProps;
  1302. SPropValue sSpoofEID = {0};
  1303. ULONG ulCmpRes = 0;
  1304. static constexpr const SizedSPropTagArray(3, sptaIDProps) =
  1305. {3, {PR_DISPLAY_NAME_W, PR_EC_SENDAS_USER_ENTRYIDS,
  1306. PR_DISPLAY_TYPE}};
  1307. ULONG cValues = 0;
  1308. hr = SMTPToZarafa(lpAddrBook, ulRepresentCB, lpRepresentEID, &sSpoofEID.Value.bin.cb, (LPENTRYID*)&sSpoofEID.Value.bin.lpb);
  1309. if (hr != hrSuccess)
  1310. hr = ContactToKopano(lpUserStore, ulRepresentCB, lpRepresentEID, &sSpoofEID.Value.bin.cb, (LPENTRYID*)&sSpoofEID.Value.bin.lpb);
  1311. if (hr == hrSuccess) {
  1312. ulRepresentCB = sSpoofEID.Value.bin.cb;
  1313. lpRepresentEID = (LPENTRYID)sSpoofEID.Value.bin.lpb;
  1314. }
  1315. // you can always send as yourself
  1316. if (lpAddrBook->CompareEntryIDs(ulOwnerCB, lpOwnerEID, ulRepresentCB, lpRepresentEID, 0, &ulCmpRes) == hrSuccess && ulCmpRes == TRUE)
  1317. {
  1318. bAllowed = true;
  1319. goto exit;
  1320. }
  1321. // representing entryid is now always a Kopano Entry ID. Open the user so we can log the display name
  1322. hr = lpAddrBook->OpenEntry(ulRepresentCB, lpRepresentEID, nullptr, 0, &ulObjType, &~lpRepresenting);
  1323. if (hr != hrSuccess) {
  1324. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "CheckSendAs(): OpenEntry failed(1) %x", hr);
  1325. goto exit;
  1326. }
  1327. hr = lpRepresenting->GetProps(sptaIDProps, 0, &cValues, &~lpRepresentProps);
  1328. if (FAILED(hr)) {
  1329. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "CheckSendAs(): GetProps failed(1) %x", hr);
  1330. goto exit;
  1331. }
  1332. hr = hrSuccess;
  1333. // Open the owner to get the displayname for logging
  1334. if (lpAddrBook->OpenEntry(ulOwnerCB, lpOwnerEID, nullptr, 0, &ulObjType, &~lpMailboxOwner) != hrSuccess) {
  1335. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "CheckSendAs(): OpenEntry failed(2) %x", hr);
  1336. goto exit;
  1337. }
  1338. hr = lpMailboxOwner->GetProps(sptaIDProps, 0, &cValues, &~lpOwnerProps);
  1339. if (FAILED(hr)) {
  1340. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "CheckSendAs(): GetProps failed(2) %x", hr);
  1341. goto exit;
  1342. }
  1343. hr = hrSuccess;
  1344. if (lpRepresentProps[2].ulPropTag != PR_DISPLAY_TYPE) { // Required property for a mailuser object
  1345. hr = MAPI_E_NOT_FOUND;
  1346. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "CheckSendAs(): PR_DISPLAY_TYPE missing %x", hr);
  1347. goto exit;
  1348. }
  1349. bHasStore = (lpRepresentProps[2].Value.l == DT_MAILUSER);
  1350. if (lpRepresentProps[1].ulPropTag != PR_EC_SENDAS_USER_ENTRYIDS)
  1351. // No sendas, therefore no sendas permissions, but we don't fail
  1352. goto exit;
  1353. hr = HrCheckAllowedEntryIDArray("sendas",
  1354. lpRepresentProps[0].ulPropTag == PR_DISPLAY_NAME_W ? lpRepresentProps[0].Value.lpszW : L"<no name>",
  1355. lpAddrBook, ulOwnerCB, lpOwnerEID,
  1356. lpRepresentProps[1].Value.MVbin.cValues, lpRepresentProps[1].Value.MVbin.lpbin, &ulObjType, &bAllowed);
  1357. if (bAllowed)
  1358. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Mail for user '%ls' is sent as %s '%ls'",
  1359. lpOwnerProps[0].ulPropTag == PR_DISPLAY_NAME_W ? lpOwnerProps[0].Value.lpszW : L"<no name>",
  1360. (ulObjType != MAPI_DISTLIST)?"user":"group",
  1361. lpRepresentProps[0].ulPropTag == PR_DISPLAY_NAME_W ? lpRepresentProps[0].Value.lpszW : L"<no name>");
  1362. exit:
  1363. if (!bAllowed) {
  1364. if (lpRepresentProps && PROP_TYPE(lpRepresentProps[0].ulPropTag) != PT_ERROR)
  1365. lpMailer->setError(_("You are not allowed to send as user or group ")+wstring(lpRepresentProps[0].Value.lpszW));
  1366. else
  1367. lpMailer->setError(_("The user or group you try to send as could not be found."));
  1368. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "User '%ls' is not allowed to send as user or group '%ls'. "
  1369. "You may enable all outgoing addresses by enabling the always_send_delegates option.",
  1370. (lpOwnerProps && PROP_TYPE(lpOwnerProps[0].ulPropTag) != PT_ERROR) ? lpOwnerProps[0].Value.lpszW : L"<unknown>",
  1371. (lpRepresentProps && PROP_TYPE(lpRepresentProps[0].ulPropTag) != PT_ERROR) ? lpRepresentProps[0].Value.lpszW : L"<unknown>");
  1372. }
  1373. if (bAllowed && bHasStore)
  1374. hr = HrOpenRepresentStore(lpAddrBook, lpUserStore, lpAdminSession, ulRepresentCB, lpRepresentEID, lppRepStore);
  1375. else
  1376. *lppRepStore = NULL;
  1377. *lpbAllowed = bAllowed;
  1378. MAPIFreeBuffer(sSpoofEID.Value.bin.lpb);
  1379. return hr;
  1380. }
  1381. /**
  1382. * Checks if the current user is a delegate of a specified user. Needs
  1383. * admin rights to open the delegate store.
  1384. *
  1385. * @param[in] lpAddrBook The Global Addressbook of the user trying to send an email.
  1386. * @param[in] lpUserStore The store of the user trying to send an email.
  1387. * @param[in] lpAdminSession MAPI session of the Kopano SYSTEM user.
  1388. * @param[in] ulOwnerCB Number of bytes in lpOwnerEID
  1389. * @param[in] lpOwnerEID EntryID of the user sending the mail.
  1390. * @param[in] ulRepresentCB Number of bytes in lpRepresentEID.
  1391. * @param[in] lpRepresentEID EntryID of the user set in the From address. Can be a One-off entryid.
  1392. * @param[out] lpbAllowed Set to true if the lpOwnerEID is a delegate of lpRepresentEID
  1393. * @param[out] lppRepStore The store of the delegate when allowed.
  1394. * @return HRESULT
  1395. * @retval hrSuccess, always returned, actual return value in lpbAllowed.
  1396. */
  1397. static HRESULT CheckDelegate(IAddrBook *lpAddrBook, IMsgStore *lpUserStore,
  1398. IMAPISession *lpAdminSession, ULONG ulOwnerCB, LPENTRYID lpOwnerEID,
  1399. ULONG ulRepresentCB, LPENTRYID lpRepresentEID, bool *lpbAllowed,
  1400. LPMDB *lppRepStore)
  1401. {
  1402. HRESULT hr = hrSuccess;
  1403. bool bAllowed = false;
  1404. ULONG ulObjType;
  1405. object_ptr<IMsgStore> lpRepStore;
  1406. memory_ptr<SPropValue> lpUserOwnerName, lpRepOwnerName;
  1407. object_ptr<IMAPIFolder> lpRepSubtree;
  1408. memory_ptr<SPropValue> lpRepFBProp, lpDelegates;
  1409. object_ptr<IMessage> lpRepFBMessage;
  1410. SPropValue sSpoofEID = {0};
  1411. hr = SMTPToZarafa(lpAddrBook, ulRepresentCB, lpRepresentEID, &sSpoofEID.Value.bin.cb, (LPENTRYID*)&sSpoofEID.Value.bin.lpb);
  1412. if (hr != hrSuccess)
  1413. hr = ContactToKopano(lpUserStore, ulRepresentCB, lpRepresentEID, &sSpoofEID.Value.bin.cb, (LPENTRYID*)&sSpoofEID.Value.bin.lpb);
  1414. if (hr == hrSuccess) {
  1415. ulRepresentCB = sSpoofEID.Value.bin.cb;
  1416. lpRepresentEID = (LPENTRYID)sSpoofEID.Value.bin.lpb;
  1417. }
  1418. hr = HrOpenRepresentStore(lpAddrBook, lpUserStore, lpAdminSession, ulRepresentCB, lpRepresentEID, &~lpRepStore);
  1419. if (hr == MAPI_E_NOT_FOUND) {
  1420. hr = hrSuccess; // No store: no delegate allowed!
  1421. goto exit;
  1422. }
  1423. else if (hr != hrSuccess) {
  1424. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "CheckDelegate() HrOpenRepresentStore failed: %x", hr);
  1425. goto exit;
  1426. }
  1427. hr = HrGetOneProp(lpUserStore, PR_MAILBOX_OWNER_NAME, &~lpUserOwnerName);
  1428. if (hr != hrSuccess)
  1429. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "CheckDelegate() PR_MAILBOX_OWNER_NAME(user) fetch failed %x", hr);
  1430. hr = HrGetOneProp(lpRepStore, PR_MAILBOX_OWNER_NAME, &~lpRepOwnerName);
  1431. if (hr != hrSuccess)
  1432. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "CheckDelegate() PR_MAILBOX_OWNER_NAME(rep) fetch failed %x", hr);
  1433. // ignore error, just a name for logging
  1434. // open root container
  1435. hr = lpRepStore->OpenEntry(0, nullptr, nullptr, 0, &ulObjType, &~lpRepSubtree);
  1436. if (hr != hrSuccess) {
  1437. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "CheckDelegate() OpenENtry(rep) failed %x", hr);
  1438. goto exit;
  1439. }
  1440. hr = HrGetOneProp(lpRepSubtree, PR_FREEBUSY_ENTRYIDS, &~lpRepFBProp);
  1441. if (hr != hrSuccess) {
  1442. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "CheckDelegate() HrGetOneProp(rep) failed %x", hr);
  1443. goto exit;
  1444. }
  1445. if (lpRepFBProp->Value.MVbin.cValues < 2) {
  1446. hr = MAPI_E_NOT_FOUND;
  1447. goto exit;
  1448. }
  1449. hr = lpRepSubtree->OpenEntry(lpRepFBProp->Value.MVbin.lpbin[1].cb, reinterpret_cast<ENTRYID *>(lpRepFBProp->Value.MVbin.lpbin[1].lpb), nullptr, 0, &ulObjType, &~lpRepFBMessage);
  1450. if (hr != hrSuccess) {
  1451. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "CheckDelegate() OpenEntry(rep) failed %x", hr);
  1452. goto exit;
  1453. }
  1454. hr = HrGetOneProp(lpRepFBMessage, PR_SCHDINFO_DELEGATE_ENTRYIDS, &~lpDelegates);
  1455. if (hr != hrSuccess) {
  1456. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "CheckDelegate() HrGetOneProp failed %x", hr);
  1457. goto exit;
  1458. }
  1459. hr = HrCheckAllowedEntryIDArray("delegate", lpRepOwnerName ? lpRepOwnerName->Value.lpszW : L"<no name>", lpAddrBook, ulOwnerCB, lpOwnerEID, lpDelegates->Value.MVbin.cValues, lpDelegates->Value.MVbin.lpbin, &ulObjType, &bAllowed);
  1460. if (hr != hrSuccess) {
  1461. ec_log_err("CheckDelegate() HrCheckAllowedEntryIDArray failed %x %s", hr, GetMAPIErrorMessage(hr));
  1462. goto exit;
  1463. }
  1464. if (bAllowed)
  1465. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Mail for user '%ls' is allowed on behalf of user '%ls'%s",
  1466. lpUserOwnerName ? lpUserOwnerName->Value.lpszW : L"<no name>",
  1467. lpRepOwnerName ? lpRepOwnerName->Value.lpszW : L"<no name>",
  1468. (ulObjType != MAPI_DISTLIST)?"":" because of group");
  1469. exit:
  1470. *lpbAllowed = bAllowed;
  1471. // when any step failed, delegate is not setup correctly, so bAllowed == false
  1472. hr = hrSuccess;
  1473. if (bAllowed)
  1474. *lppRepStore = lpRepStore.release();
  1475. MAPIFreeBuffer(sSpoofEID.Value.bin.lpb);
  1476. return hr;
  1477. }
  1478. /**
  1479. * Copies the sent message to the delegate store. Returns the copy of lpMessage.
  1480. *
  1481. * @param[in] lpMessage The message to be copied to the delegate store in that "Sent Items" folder.
  1482. * @param[in] lpRepStore The store of the delegate where the message will be copied.
  1483. * @param[out] lppRepMessage The new message in the delegate store.
  1484. * @return HRESULT
  1485. */
  1486. static HRESULT CopyDelegateMessageToSentItems(LPMESSAGE lpMessage,
  1487. LPMDB lpRepStore, LPMESSAGE *lppRepMessage)
  1488. {
  1489. HRESULT hr = hrSuccess;
  1490. memory_ptr<SPropValue> lpSentItemsEntryID;
  1491. object_ptr<IMAPIFolder> lpSentItems;
  1492. ULONG ulObjType;
  1493. object_ptr<IMessage> lpDestMsg;
  1494. SPropValue sProp[1];
  1495. hr = HrGetOneProp(lpRepStore, PR_IPM_SENTMAIL_ENTRYID, &~lpSentItemsEntryID);
  1496. if (hr != hrSuccess) {
  1497. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to find representee's sent items folder: error 0x%08X", hr);
  1498. return hr;
  1499. }
  1500. hr = lpRepStore->OpenEntry(lpSentItemsEntryID->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpSentItemsEntryID->Value.bin.lpb),
  1501. &IID_IMAPIFolder, MAPI_BEST_ACCESS, &ulObjType, &~lpSentItems);
  1502. if (hr != hrSuccess) {
  1503. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to open representee's sent items folder: error 0x%08X", hr);
  1504. return hr;
  1505. }
  1506. hr = lpSentItems->CreateMessage(nullptr, 0, &~lpDestMsg);
  1507. if (hr != hrSuccess) {
  1508. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to create representee's message: error 0x%08X", hr);
  1509. return hr;
  1510. }
  1511. hr = lpMessage->CopyTo(0, NULL, NULL, 0, NULL, &IID_IMessage, (LPVOID)lpDestMsg, 0, NULL);
  1512. if (FAILED(hr)) {
  1513. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to copy representee's message: error 0x%08X", hr);
  1514. return hr;
  1515. }
  1516. sProp[0].ulPropTag = PR_MESSAGE_FLAGS;
  1517. sProp[0].Value.ul = MSGFLAG_READ;
  1518. hr = lpDestMsg->SetProps(1, sProp, NULL);
  1519. if (hr != hrSuccess) {
  1520. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to edit representee's message: error 0x%08X", hr);
  1521. return hr;
  1522. }
  1523. *lppRepMessage = lpDestMsg.release();
  1524. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Copy placed in representee's sent items folder");
  1525. return hrSuccess;
  1526. }
  1527. /**
  1528. * Delete the message from the outgoing queue. Should always be
  1529. * called, unless the message should be retried later (SMTP server
  1530. * temporarily not available or timed message).
  1531. *
  1532. * @param[in] cbEntryId Number of bytes in lpEntryId
  1533. * @param[in] lpEntryId EntryID of the message to remove from outgoing queue.
  1534. * @param[in] lpMsgStore Message store of the user containing the message of lpEntryId
  1535. * @return HRESULT
  1536. */
  1537. static HRESULT PostSendProcessing(ULONG cbEntryId, const ENTRYID *lpEntryId,
  1538. IMsgStore *lpMsgStore)
  1539. {
  1540. HRESULT hr = hrSuccess;
  1541. memory_ptr<SPropValue> lpObject;
  1542. object_ptr<IECSpooler> lpSpooler;
  1543. hr = HrGetOneProp(lpMsgStore, PR_EC_OBJECT, &~lpObject);
  1544. if(hr != hrSuccess) {
  1545. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get PR_EC_OBJECT in post-send processing: 0x%08X", hr);
  1546. return hr;
  1547. }
  1548. hr = ((IECUnknown *)lpObject->Value.lpszA)->QueryInterface(IID_IECSpooler, &~lpSpooler);
  1549. if(hr != hrSuccess) {
  1550. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get spooler interface for message: 0x%08X", hr);
  1551. return hr;
  1552. }
  1553. hr = lpSpooler->DeleteFromMasterOutgoingTable(cbEntryId, lpEntryId, EC_SUBMIT_MASTER);
  1554. if (hr != hrSuccess)
  1555. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Could not remove invalid message from queue, error code: 0x%08X", hr);
  1556. return hr;
  1557. }
  1558. /**
  1559. * Using the given resources, sends the mail to the SMTP server.
  1560. *
  1561. * @param[in] lpAdminSession Kopano SYSTEM user MAPI session.
  1562. * @param[in] lpUserSession MAPI Session of the user sending the mail.
  1563. * @param[in] lpServiceAdmin IECServiceAdmin interface on the user's store.
  1564. * @param[in] lpSecurity IECSecurity interface on the user's store.
  1565. * @param[in] lpUserStore The IMsgStore interface of the user's store.
  1566. * @param[in] lpAddrBook The Global Addressbook of the user.
  1567. * @param[in] lpMailer ECSender object (inetmapi), used to send the mail.
  1568. * @param[in] cbMsgEntryId Number of bytes in lpMsgEntryId
  1569. * @param[in] lpMsgEntryId EntryID of the message to be send.
  1570. * @param[out] lppMessage The message that processed. Always returned if opened.
  1571. *
  1572. * @note The mail will be removed by the calling process when we return an error, except for the errors/warnings listed below.
  1573. * @retval hrSuccess Mail was successful sent moved when when needed.
  1574. * @retval MAPI_E_WAIT Mail has a specific timestamp when it should be sent.
  1575. * @retval MAPI_W_NO_SERVICE The SMTP server is not responding correctly.
  1576. */
  1577. static HRESULT ProcessMessage(IMAPISession *lpAdminSession,
  1578. IMAPISession *lpUserSession, IECServiceAdmin *lpServiceAdmin,
  1579. IECSecurity *lpSecurity, IMsgStore *lpUserStore, IAddrBook *lpAddrBook,
  1580. ECSender *lpMailer, ULONG cbMsgEntryId, LPENTRYID lpMsgEntryId,
  1581. IMessage **lppMessage)
  1582. {
  1583. HRESULT hr = hrSuccess;
  1584. object_ptr<IMessage> lpMessage;
  1585. ULONG ulObjType = 0;
  1586. ULONG cbOwner = 0;
  1587. memory_ptr<ENTRYID> lpOwner;
  1588. memory_ptr<ECUSER> lpUser;
  1589. SPropValue sPropSender[4];
  1590. static constexpr const SizedSPropTagArray(5, sptaMoveReprProps) =
  1591. {5, {PR_SENT_REPRESENTING_NAME_W,
  1592. PR_SENT_REPRESENTING_ADDRTYPE_W,
  1593. PR_SENT_REPRESENTING_EMAIL_ADDRESS_W,
  1594. PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_SEARCH_KEY}};
  1595. memory_ptr<SPropValue> lpMoveReprProps, lpPropOwner;
  1596. ULONG cValuesMoveProps = 0;
  1597. bool bAllowSendAs = false;
  1598. bool bAllowDelegate = false;
  1599. ULONG ulCmpRes = 0;
  1600. object_ptr<IMsgStore> lpRepStore;
  1601. object_ptr<IMessage> lpRepMessage;
  1602. memory_ptr<SPropValue> lpRepEntryID, lpSubject, lpMsgSize;
  1603. memory_ptr<SPropValue> lpAutoForward, lpMsgClass, lpDeferSendTime;
  1604. PyMapiPluginFactory pyMapiPluginFactory;
  1605. std::unique_ptr<pym_plugin_intf> ptrPyMapiPlugin;
  1606. ULONG ulResult = 0;
  1607. ArchiveResult archiveResult;
  1608. sending_options sopt;
  1609. imopt_default_sending_options(&sopt);
  1610. // When sending messages, we want to minimize the use of tnef.
  1611. // In case always_send_tnef is set to yes, we force tnef, otherwise we
  1612. // minimize (set to no or minimal).
  1613. if (!strcmp(g_lpConfig->GetSetting("always_send_tnef"), "minimal") ||
  1614. !parseBool(g_lpConfig->GetSetting("always_send_tnef")))
  1615. sopt.use_tnef = -1;
  1616. else
  1617. sopt.use_tnef = 1;
  1618. sopt.force_utf8 = parseBool(g_lpConfig->GetSetting("always_send_utf8"));
  1619. sopt.allow_send_to_everyone = parseBool(g_lpConfig->GetSetting("allow_send_to_everyone"));
  1620. // Enable SMTP Delivery Status Notifications
  1621. sopt.enable_dsn = parseBool(g_lpConfig->GetSetting("enable_dsn"));
  1622. sopt.always_expand_distr_list = parseBool(g_lpConfig->GetSetting("expand_groups"));
  1623. // Init plugin system
  1624. hr = pyMapiPluginFactory.create_plugin(g_lpConfig, g_lpLogger, "SpoolerPluginManager", &unique_tie(ptrPyMapiPlugin));
  1625. if (hr != hrSuccess) {
  1626. ec_log_crit("K-1733: Unable to initialize the spooler plugin system: %s (%x).",
  1627. GetMAPIErrorMessage(hr), hr);
  1628. hr = MAPI_E_CALL_FAILED;
  1629. goto exit;
  1630. }
  1631. // Get the owner of the store
  1632. hr = lpSecurity->GetOwner(&cbOwner, &~lpOwner);
  1633. if (hr != hrSuccess) {
  1634. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get owner information, error code: 0x%08X", hr);
  1635. goto exit;
  1636. }
  1637. // We now have the owner ID, get the owner information through the ServiceAdmin
  1638. hr = lpServiceAdmin->GetUser(cbOwner, lpOwner, MAPI_UNICODE, &~lpUser);
  1639. if (hr != hrSuccess) {
  1640. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get user information from store, error code: 0x%08X", hr);
  1641. goto exit;
  1642. }
  1643. // open the message we need to send
  1644. hr = lpUserStore->OpenEntry(cbMsgEntryId, reinterpret_cast<ENTRYID *>(lpMsgEntryId), &IID_IMessage, MAPI_BEST_ACCESS, &ulObjType, &~lpMessage);
  1645. if (hr != hrSuccess) {
  1646. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Could not open message in store from user %ls: %s (%x)",
  1647. lpUser->lpszUsername, GetMAPIErrorMessage(hr), hr);
  1648. goto exit;
  1649. }
  1650. /* Get subject for logging - ignore errors, we check for nullptr. */
  1651. HrGetOneProp(lpMessage, PR_SUBJECT_W, &~lpSubject);
  1652. HrGetOneProp(lpMessage, PR_MESSAGE_SIZE, &~lpMsgSize);
  1653. HrGetOneProp(lpMessage, PR_DEFERRED_SEND_TIME, &~lpDeferSendTime);
  1654. // do we need to send the message already?
  1655. if (lpDeferSendTime) {
  1656. // check time
  1657. time_t now = time(NULL);
  1658. time_t sendat;
  1659. FileTimeToUnixTime(lpDeferSendTime->Value.ft, &sendat);
  1660. if (now < sendat) {
  1661. // should actually be logged just once .. but how?
  1662. struct tm tmp;
  1663. char timestring[256];
  1664. localtime_r(&sendat, &tmp);
  1665. strftime(timestring, 256, "%c", &tmp);
  1666. g_lpLogger->Log(EC_LOGLEVEL_INFO, "E-mail for user %ls, subject '%ls', should be sent later at '%s'",
  1667. lpUser->lpszUsername, lpSubject ? lpSubject->Value.lpszW : L"<none>", timestring);
  1668. hr = MAPI_E_WAIT;
  1669. goto exit;
  1670. }
  1671. }
  1672. // fatal, all other log messages are otherwise somewhat meaningless
  1673. if (g_lpLogger->Log(EC_LOGLEVEL_DEBUG))
  1674. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Sending e-mail for user %ls, subject: '%ls', size: %d",
  1675. lpUser->lpszUsername, lpSubject ? lpSubject->Value.lpszW : L"<none>",
  1676. lpMsgSize ? lpMsgSize->Value.ul : 0);
  1677. else
  1678. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Sending e-mail for user %ls, size: %d",
  1679. lpUser->lpszUsername, lpMsgSize ? lpMsgSize->Value.ul : 0);
  1680. /*
  1681. PR_SENDER_* maps to Sender:
  1682. PR_SENT_REPRESENTING_* maps to From:
  1683. Sender: field is optional, From: is mandatory
  1684. PR_SENDER_* is mandatory, and always set by us (will be overwritten if was set)
  1685. PR_SENT_REPRESENTING_* is optional, and set by outlook when the user modifies the From in outlook.
  1686. */
  1687. // Set PR_SENT_REPRESENTING, as this is set on all 'sent' items and is the column
  1688. // that is shown by default in Outlook's 'sent items' folder
  1689. if (HrGetOneProp(lpMessage, PR_SENT_REPRESENTING_ENTRYID, &~lpRepEntryID) != hrSuccess) {
  1690. // set current user as sender (From header)
  1691. sPropSender[0].ulPropTag = PR_SENT_REPRESENTING_NAME_W;
  1692. sPropSender[0].Value.lpszW = (LPTSTR)lpUser->lpszFullName;
  1693. sPropSender[1].ulPropTag = PR_SENT_REPRESENTING_ADDRTYPE_W;
  1694. sPropSender[1].Value.lpszW = (LPTSTR)L"ZARAFA";
  1695. sPropSender[2].ulPropTag = PR_SENT_REPRESENTING_EMAIL_ADDRESS_W;
  1696. sPropSender[2].Value.lpszW = (LPTSTR)lpUser->lpszMailAddress;
  1697. sPropSender[3].ulPropTag = PR_SENT_REPRESENTING_ENTRYID;
  1698. sPropSender[3].Value.bin.cb = lpUser->sUserId.cb;
  1699. sPropSender[3].Value.bin.lpb = lpUser->sUserId.lpb;
  1700. HRESULT hr2 = lpMessage->SetProps(4, sPropSender, NULL);
  1701. if (hr2 != hrSuccess) {
  1702. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to set sender id for message: %s (%x)",
  1703. GetMAPIErrorMessage(hr2), hr2);
  1704. goto exit;
  1705. }
  1706. }
  1707. // requested that mail is sent as somebody else
  1708. // since we can have SMTP and ZARAFA entry IDs, we will open it, and get the
  1709. // If this is a forwarded e-mail, then allow sending as the original sending e-mail address. Note that
  1710. // this can be misused by MAPI client that just set PR_AUTO_FORWARDED. Since it would have been just as
  1711. // easy for the client just to spoof their 'from' address via SMTP, we're allowing this for now. You can
  1712. // completely turn it off via the 'allow_redirect_spoofing' setting.
  1713. else if (strcmp(g_lpConfig->GetSetting("allow_redirect_spoofing"), "yes") == 0 &&
  1714. HrGetOneProp(lpMessage, PR_AUTO_FORWARDED, &~lpAutoForward) == hrSuccess &&
  1715. lpAutoForward->Value.b) {
  1716. bAllowSendAs = true;
  1717. } else {
  1718. hr = HrGetOneProp(lpUserStore, PR_MAILBOX_OWNER_ENTRYID, &~lpPropOwner);
  1719. if (hr != hrSuccess) {
  1720. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get Kopano mailbox owner id, error code: 0x%08X", hr);
  1721. goto exit;
  1722. }
  1723. hr = lpAddrBook->CompareEntryIDs(lpPropOwner->Value.bin.cb, (LPENTRYID)lpPropOwner->Value.bin.lpb,
  1724. lpRepEntryID->Value.bin.cb, (LPENTRYID)lpRepEntryID->Value.bin.lpb, 0, &ulCmpRes);
  1725. if (hr == hrSuccess && ulCmpRes == FALSE) {
  1726. if (strcmp(g_lpConfig->GetSetting("always_send_delegates"), "yes") == 0) {
  1727. // pre 6.20 behaviour
  1728. bAllowDelegate = true;
  1729. HrOpenRepresentStore(lpAddrBook, lpUserStore, lpAdminSession, lpRepEntryID->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpRepEntryID->Value.bin.lpb), &~lpRepStore);
  1730. // ignore error if unable to open, just the copy of the mail might possibily not be done.
  1731. } else if(strcmp(g_lpConfig->GetSetting("allow_delegate_meeting_request"), "yes") == 0 &&
  1732. HrGetOneProp(lpMessage, PR_MESSAGE_CLASS_A, &~lpMsgClass) == hrSuccess &&
  1733. ((strcasecmp(lpMsgClass->Value.lpszA, "IPM.Schedule.Meeting.Request" ) == 0) ||
  1734. (strcasecmp(lpMsgClass->Value.lpszA, "IPM.Schedule.Meeting.Canceled" ) == 0))) {
  1735. // Meeting request can always sent as 'on behalf of' (Zarafa and SMTP user).
  1736. // This is needed if a user forward a meeting request. If you have permissions on a calendar,
  1737. // you can always sent with 'on behalve of'. This behavior is like exchange.
  1738. bAllowDelegate = true;
  1739. } else {
  1740. hr = CheckDelegate(lpAddrBook, lpUserStore, lpAdminSession, lpPropOwner->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpPropOwner->Value.bin.lpb),
  1741. lpRepEntryID->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpRepEntryID->Value.bin.lpb), &bAllowDelegate, &~lpRepStore);
  1742. if (hr != hrSuccess)
  1743. goto exit;
  1744. }
  1745. if (!bAllowDelegate) {
  1746. hr = CheckSendAs(lpAddrBook, lpUserStore, lpAdminSession, lpMailer, lpPropOwner->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpPropOwner->Value.bin.lpb),
  1747. lpRepEntryID->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpRepEntryID->Value.bin.lpb), &bAllowSendAs, &~lpRepStore);
  1748. if (hr != hrSuccess)
  1749. goto exit;
  1750. if (!bAllowSendAs) {
  1751. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "E-mail for user %ls may not be sent, notifying user", lpUser->lpszUsername);
  1752. HRESULT hr2 = SendUndeliverable(lpMailer, lpUserStore, lpMessage);
  1753. if (hr2 != hrSuccess)
  1754. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to create undeliverable message for user %ls: %s (%x)",
  1755. lpUser->lpszUsername, GetMAPIErrorMessage(hr2), hr2);
  1756. // note: hr == hrSuccess, parent process will not send the undeliverable too
  1757. goto exit;
  1758. }
  1759. // else {}: we are allowed to directly send
  1760. }
  1761. // else {}: allowed with 'on behalf of'
  1762. }
  1763. // else {}: owner and representing are the same, send as normal mail
  1764. }
  1765. // put storeowner info in PR_SENDER_ props, forces correct From data
  1766. sPropSender[0].ulPropTag = PR_SENDER_NAME_W;
  1767. sPropSender[0].Value.LPSZ = lpUser->lpszFullName;
  1768. sPropSender[1].ulPropTag = PR_SENDER_ADDRTYPE_W;
  1769. sPropSender[1].Value.LPSZ = const_cast<TCHAR *>(_T("ZARAFA"));
  1770. sPropSender[2].ulPropTag = PR_SENDER_EMAIL_ADDRESS_W;
  1771. sPropSender[2].Value.LPSZ = lpUser->lpszMailAddress;
  1772. sPropSender[3].ulPropTag = PR_SENDER_ENTRYID;
  1773. sPropSender[3].Value.bin.cb = lpUser->sUserId.cb;
  1774. sPropSender[3].Value.bin.lpb = lpUser->sUserId.lpb;
  1775. // @todo PR_SENDER_SEARCH_KEY
  1776. hr = lpMessage->SetProps(4, sPropSender, NULL);
  1777. if (hr != hrSuccess) {
  1778. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to update message with sender: %s (%x)",
  1779. GetMAPIErrorMessage(hr), hr);
  1780. goto exit;
  1781. }
  1782. hr = lpMessage->SaveChanges(KEEP_OPEN_READWRITE);
  1783. if (hr != hrSuccess) {
  1784. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to save message before sending: %s (%x)",
  1785. GetMAPIErrorMessage(hr), hr);
  1786. goto exit;
  1787. }
  1788. if (lpRepStore != nullptr &&
  1789. parseBool(g_lpConfig->GetSetting("copy_delegate_mails", NULL, "yes")))
  1790. // copy the original message with the actual sender data
  1791. // so you see the "on behalf of" in the sent-items version, even when send-as is used (see below)
  1792. CopyDelegateMessageToSentItems(lpMessage, lpRepStore, &~lpRepMessage);
  1793. // possible error is logged in function.
  1794. if (bAllowSendAs) {
  1795. // move PR_REPRESENTING to PR_SENDER_NAME
  1796. hr = lpMessage->GetProps(sptaMoveReprProps, 0, &cValuesMoveProps, &~lpMoveReprProps);
  1797. if (FAILED(hr)) {
  1798. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to find sender information: %s (%x)",
  1799. GetMAPIErrorMessage(hr), hr);
  1800. goto exit;
  1801. }
  1802. hr = lpMessage->DeleteProps(sptaMoveReprProps, NULL);
  1803. if (FAILED(hr)) {
  1804. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to remove sender information: %s (%x)",
  1805. GetMAPIErrorMessage(hr), hr);
  1806. goto exit;
  1807. }
  1808. lpMoveReprProps[0].ulPropTag = PROP_TAG(PROP_TYPE(lpMoveReprProps[0].ulPropTag), PROP_ID(PR_SENDER_NAME_W));
  1809. lpMoveReprProps[1].ulPropTag = PROP_TAG(PROP_TYPE(lpMoveReprProps[1].ulPropTag), PROP_ID(PR_SENDER_ADDRTYPE_W));
  1810. lpMoveReprProps[2].ulPropTag = PROP_TAG(PROP_TYPE(lpMoveReprProps[2].ulPropTag), PROP_ID(PR_SENDER_EMAIL_ADDRESS_W));
  1811. lpMoveReprProps[3].ulPropTag = PROP_TAG(PROP_TYPE(lpMoveReprProps[3].ulPropTag), PROP_ID(PR_SENDER_ENTRYID));
  1812. lpMoveReprProps[4].ulPropTag = PROP_TAG(PROP_TYPE(lpMoveReprProps[4].ulPropTag), PROP_ID(PR_SENDER_SEARCH_KEY));
  1813. hr = lpMessage->SetProps(5, lpMoveReprProps, NULL);
  1814. if (FAILED(hr)) {
  1815. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to update sender information: %s (%x)",
  1816. GetMAPIErrorMessage(hr), hr);
  1817. goto exit;
  1818. }
  1819. /*
  1820. * Note: do not save these changes!
  1821. *
  1822. * If we're sending through Outlook, we're sending a copy of
  1823. * the message from the root container. Changes to this
  1824. * message make no sense, since it's deleted anyway.
  1825. *
  1826. * If we're sending through WebAccess, we're sending the real
  1827. * message, and bDoSentMail is true. This will move the
  1828. * message to the users sent-items folder (using the entryid
  1829. * from the message) and move it using its entryid. Since we
  1830. * didn't save these changes, the original unmodified version
  1831. * will be moved to the sent-items folder, and that will show
  1832. * the correct From/Sender data.
  1833. */
  1834. }
  1835. if (sopt.always_expand_distr_list) {
  1836. // Expand recipients with ADDRTYPE=ZARAFA to multiple ADDRTYPE=SMTP recipients
  1837. hr = ExpandRecipients(lpAddrBook, lpMessage);
  1838. if(hr != hrSuccess)
  1839. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to expand message recipient groups: %s (%x)",
  1840. GetMAPIErrorMessage(hr), hr);
  1841. }
  1842. hr = RewriteRecipients(lpUserSession, lpMessage);
  1843. if (hr != hrSuccess)
  1844. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to rewrite recipients: %s (%x)",
  1845. GetMAPIErrorMessage(hr), hr);
  1846. if (sopt.always_expand_distr_list) {
  1847. // Only touch recips if we're expanding groups; the rationale is here that the user
  1848. // has typed a recipient twice if we have duplicates and expand_groups = no, so that's
  1849. // what the user wanted apparently. What's more, duplicate recips are filtered for RCPT TO
  1850. // later.
  1851. hr = UniqueRecipients(lpMessage);
  1852. if (hr != hrSuccess)
  1853. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to remove duplicate recipients: %s (%x)",
  1854. GetMAPIErrorMessage(hr), hr);
  1855. }
  1856. RewriteQuotedRecipients(lpMessage);
  1857. hr = ptrPyMapiPlugin->MessageProcessing("PreSending", lpUserSession, lpAddrBook, lpUserStore, NULL, lpMessage, &ulResult);
  1858. if (hr != hrSuccess)
  1859. goto exit;
  1860. if (ulResult == MP_RETRY_LATER) {
  1861. hr = MAPI_E_WAIT;
  1862. goto exit;
  1863. } else if (ulResult == MP_FAILED) {
  1864. g_lpLogger->Log(EC_LOGLEVEL_CRIT, "Plugin error, hook gives a failed error: %s (%x).",
  1865. GetMAPIErrorMessage(ulResult), ulResult);
  1866. hr = MAPI_E_CANCEL;
  1867. goto exit;
  1868. }
  1869. // Archive the message
  1870. if (parseBool(g_lpConfig->GetSetting("archive_on_send"))) {
  1871. ArchivePtr ptrArchive;
  1872. hr = Archive::Create(lpAdminSession, &ptrArchive);
  1873. if (hr != hrSuccess) {
  1874. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to instantiate archive object: 0x%08X", hr);
  1875. goto exit;
  1876. }
  1877. hr = ptrArchive->HrArchiveMessageForSending(lpMessage, &archiveResult);
  1878. if (hr != hrSuccess) {
  1879. if (ptrArchive->HaveErrorMessage())
  1880. lpMailer->setError(ptrArchive->GetErrorMessage());
  1881. goto exit;
  1882. }
  1883. }
  1884. // Now hand message to library which will send it, inetmapi will handle addressbook
  1885. hr = IMToINet(lpUserSession, lpAddrBook, lpMessage, lpMailer, sopt);
  1886. // log using fatal, all other log messages are otherwise somewhat meaningless
  1887. if (hr == MAPI_W_NO_SERVICE) {
  1888. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to connect to SMTP server, retrying mail for user %ls later", lpUser->lpszUsername);
  1889. goto exit;
  1890. } else if (hr != hrSuccess) {
  1891. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "E-mail for user %ls could not be sent, notifying user: %s (%x)",
  1892. lpUser->lpszUsername, GetMAPIErrorMessage(hr), hr);
  1893. hr = SendUndeliverable(lpMailer, lpUserStore, lpMessage);
  1894. if (hr != hrSuccess)
  1895. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to create undeliverable message for user %ls: %s (%x)",
  1896. lpUser->lpszUsername, GetMAPIErrorMessage(hr), hr);
  1897. // we set hr to success, so the parent process does not create the undeliverable thing again
  1898. hr = hrSuccess;
  1899. goto exit;
  1900. } else {
  1901. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "E-mail for user %ls was accepted by SMTP server", lpUser->lpszUsername);
  1902. }
  1903. // If we have a repsenting message, save that now in the sent-items of that user
  1904. if (lpRepMessage) {
  1905. HRESULT hr2 = lpRepMessage->SaveChanges(0);
  1906. if (hr2 != hrSuccess)
  1907. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Representee's mail copy could not be saved: %s (%x)",
  1908. GetMAPIErrorMessage(hr2), hr2);
  1909. }
  1910. exit:
  1911. if (FAILED(hr))
  1912. archiveResult.Undo(lpAdminSession);
  1913. // We always return the processes message to the caller, whether it failed or not
  1914. if (lpMessage)
  1915. lpMessage->QueryInterface(IID_IMessage, (void**)lppMessage);
  1916. return hr;
  1917. }
  1918. /**
  1919. * Entry point, sends the mail for a user. Most of the time, it will
  1920. * also move the sent mail to the "Sent Items" folder of the user.
  1921. *
  1922. * @param[in] szUsername The username to login as. This name is in unicode.
  1923. * @param[in] szSMTP The SMTP server name or IP address to use.
  1924. * @param[in] szPath The URI to the Kopano server.
  1925. * @param[in] cbMsgEntryId The number of bytes in lpMsgEntryId
  1926. * @param[in] lpMsgEntryId The EntryID of the message to send
  1927. * @param[in] bDoSentMail true if the mail should be moved to the "Sent Items" folder of the user.
  1928. * @return HRESULT
  1929. */
  1930. HRESULT ProcessMessageForked(const wchar_t *szUsername, const char *szSMTP,
  1931. int ulPort, const char *szPath, ULONG cbMsgEntryId, LPENTRYID lpMsgEntryId,
  1932. bool bDoSentMail)
  1933. {
  1934. HRESULT hr = hrSuccess;
  1935. object_ptr<IMAPISession> lpAdminSession, lpUserSession;
  1936. object_ptr<IAddrBook> lpAddrBook;
  1937. std::unique_ptr<ECSender> lpMailer;
  1938. object_ptr<IMsgStore> lpUserStore;
  1939. object_ptr<IECServiceAdmin> lpServiceAdmin;
  1940. object_ptr<IECSecurity> lpSecurity;
  1941. memory_ptr<SPropValue> lpsProp;
  1942. object_ptr<IMessage> lpMessage;
  1943. lpMailer.reset(CreateSender(szSMTP, ulPort));
  1944. if (!lpMailer) {
  1945. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  1946. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "ProcessMessageForked(): CreateSender failed: %s (%x)",
  1947. GetMAPIErrorMessage(hr), hr);
  1948. goto exit;
  1949. }
  1950. // The Admin session is used for checking delegates and archiving
  1951. hr = HrOpenECAdminSession(&~lpAdminSession, "spooler/mailer:admin",
  1952. PROJECT_SVN_REV_STR, szPath, EC_PROFILE_FLAGS_NO_PUBLIC_STORE,
  1953. g_lpConfig->GetSetting("sslkey_file", "", NULL),
  1954. g_lpConfig->GetSetting("sslkey_pass", "", NULL));
  1955. if (hr != hrSuccess) {
  1956. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open admin session: %s (%x)",
  1957. GetMAPIErrorMessage(hr), hr);
  1958. goto exit;
  1959. }
  1960. /*
  1961. * For proper group expansion, we'll need to login as the
  1962. * user. When sending an email to group 'Everyone' it should not
  1963. * be possible to send the email to users that cannot be viewed
  1964. * (because they are in a different company). By using a
  1965. * usersession for email sending we will let the server handle all
  1966. * permissions and can correctly resolve everything.
  1967. */
  1968. hr = HrOpenECSession(&~lpUserSession, "spooler/mailer",
  1969. PROJECT_SVN_REV_STR, szUsername, L"", szPath,
  1970. EC_PROFILE_FLAGS_NO_PUBLIC_STORE,
  1971. g_lpConfig->GetSetting("sslkey_file", "", NULL),
  1972. g_lpConfig->GetSetting("sslkey_pass", "", NULL));
  1973. if (hr != hrSuccess) {
  1974. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open user session: %s (%x)",
  1975. GetMAPIErrorMessage(hr), hr);
  1976. goto exit;
  1977. }
  1978. hr = lpUserSession->OpenAddressBook(0, nullptr, AB_NO_DIALOG, &~lpAddrBook);
  1979. if (hr != hrSuccess) {
  1980. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open addressbook. %s (%x)",
  1981. GetMAPIErrorMessage(hr), hr);
  1982. goto exit;
  1983. }
  1984. hr = HrOpenDefaultStore(lpUserSession, &~lpUserStore);
  1985. if (hr != hrSuccess) {
  1986. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open default store of user: %s (%x)",
  1987. GetMAPIErrorMessage(hr), hr);
  1988. goto exit;
  1989. }
  1990. hr = HrGetOneProp(lpUserStore, PR_EC_OBJECT, &~lpsProp);
  1991. if (hr != hrSuccess) {
  1992. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get Kopano internal object: %s (%x)",
  1993. GetMAPIErrorMessage(hr), hr);
  1994. goto exit;
  1995. }
  1996. // NOTE: object is placed in Value.lpszA, not Value.x
  1997. hr = ((IECUnknown *)lpsProp->Value.lpszA)->QueryInterface(IID_IECServiceAdmin, &~lpServiceAdmin);
  1998. if (hr != hrSuccess) {
  1999. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ServiceAdmin interface not supported: %s (%x)",
  2000. GetMAPIErrorMessage(hr), hr);
  2001. goto exit;
  2002. }
  2003. hr = ((IECUnknown *)lpsProp->Value.lpszA)->QueryInterface(IID_IECSecurity, &~lpSecurity);
  2004. if (hr != hrSuccess) {
  2005. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "IID_IECSecurity not supported by store: %s (%x)",
  2006. GetMAPIErrorMessage(hr), hr);
  2007. goto exit;
  2008. }
  2009. hr = ProcessMessage(lpAdminSession, lpUserSession, lpServiceAdmin,
  2010. lpSecurity, lpUserStore, lpAddrBook, lpMailer.get(), cbMsgEntryId,
  2011. lpMsgEntryId, &~lpMessage);
  2012. if (hr != hrSuccess && hr != MAPI_E_WAIT && hr != MAPI_W_NO_SERVICE && lpMessage) {
  2013. // use lpMailer to set body in SendUndeliverable
  2014. if (!lpMailer->haveError())
  2015. lpMailer->setError(_("Error found while trying to send your message. Error code: ") + wstringify(hr,true));
  2016. hr = SendUndeliverable(lpMailer.get(), lpUserStore, lpMessage);
  2017. if (hr != hrSuccess) {
  2018. // dont make parent complain too
  2019. hr = hrSuccess;
  2020. goto exit;
  2021. }
  2022. }
  2023. exit:
  2024. // The following code is after the exit tag because we *always* want to clean up the message from the outgoing queue, not
  2025. // just when it was sent correctly. This also means we should do post-sending processing (DoSentMail()).
  2026. // Ignore error, we want to give the possible failed hr back to the main process. Logging is already done.
  2027. if (hr != MAPI_W_NO_SERVICE && hr != MAPI_E_WAIT) {
  2028. if (lpMsgEntryId && lpUserStore)
  2029. PostSendProcessing(cbMsgEntryId, lpMsgEntryId, lpUserStore);
  2030. if (bDoSentMail && lpUserSession && lpMessage) {
  2031. DoSentMail(NULL, lpUserStore, 0, lpMessage);
  2032. lpMessage.release(); // fed into DoSentMail
  2033. }
  2034. }
  2035. return hr;
  2036. }