DAgent.cpp 134 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899
  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. /*
  18. * E-mail is delivered to the client through this program; it is invoked
  19. * by the MTA with the username and rfc822 e-mail message, and the delivery
  20. * agent parses the rfc822 message, setting properties on the MAPI object
  21. * as it goes along.
  22. *
  23. * The delivery agent should be called with sufficient privileges to be
  24. * able to open other users' inboxes.
  25. *
  26. * The actual decoding is done by the inetmapi library.
  27. */
  28. /*
  29. * An LMTP reply contains:
  30. * <SMTP code> <ESMTP code> <message>
  31. * See RFC 1123 and 2821 for normal codes, 1893 and 2034 for enhanced codes.
  32. *
  33. * Enhanced Delivery status codes: Class.Subject.Detail
  34. *
  35. * Classes:
  36. * 2 = Success, 4 = Persistent Transient Failure, 5 = Permanent Failure
  37. *
  38. * Subjects:
  39. * 0 = Other/Unknown, 1 = Addressing, 2 = Mailbox, 3 = Mail system
  40. * 4 = Network/Routing, 5 = Mail delivery, 6 = Message content, 7 = Security
  41. *
  42. * Detail:
  43. * see rfc.
  44. */
  45. #include <kopano/platform.h>
  46. #include <memory>
  47. #include <unordered_set>
  48. #include <climits>
  49. #include <cstdio>
  50. #include <cstdlib>
  51. #include <iostream>
  52. #include <algorithm>
  53. #include <map>
  54. #include <poll.h>
  55. #include <kopano/ECRestriction.h>
  56. #include <kopano/MAPIErrors.h>
  57. #include <kopano/mapi_ptr.h>
  58. #include <kopano/memory.hpp>
  59. #include <kopano/tie.hpp>
  60. #include "fileutil.h"
  61. #include "PyMapiPlugin.h"
  62. #include <cerrno>
  63. #include <sys/types.h>
  64. #include <sys/stat.h>
  65. #include <unistd.h>
  66. #include <sys/mman.h>
  67. #include <pwd.h>
  68. #include "spmain.h"
  69. #include "TmpPath.h"
  70. /*
  71. This is actually from sysexits.h
  72. but since those windows lamers don't have it ..
  73. let's copy some defines here..
  74. */
  75. #define EX_OK 0 /* successful termination */
  76. #define EX__BASE 64 /* base value for error messages */
  77. #define EX_USAGE 64 /* command line usage error */
  78. #define EX_SOFTWARE 70 /* internal software error */
  79. #define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
  80. #define USES_IID_IMAPIFolder
  81. #define USES_IID_IExchangeManageStore
  82. #define USES_IID_IMsgStore
  83. #include <kopano/ECGuid.h>
  84. #include <inetmapi/inetmapi.h>
  85. #include <mapi.h>
  86. #include <mapix.h>
  87. #include <mapiutil.h>
  88. #include <mapidefs.h>
  89. #include <kopano/mapiext.h>
  90. #include <mapiguid.h>
  91. #include <edkguid.h>
  92. #include <edkmdb.h>
  93. #include <kopano/EMSAbTag.h>
  94. #include <cctype>
  95. #include <ctime>
  96. #include <kopano/stringutil.h>
  97. #include <kopano/CommonUtil.h>
  98. #include <kopano/Util.h>
  99. #include <kopano/ECLogger.h>
  100. #include <kopano/my_getopt.h>
  101. #include "rules.h"
  102. #include "archive.h"
  103. #include "helpers/MAPIPropHelper.h"
  104. #include <inetmapi/options.h>
  105. #include <kopano/charset/convert.h>
  106. #include <kopano/IECServiceAdmin.h>
  107. #include <kopano/IECUnknown.h>
  108. #include <kopano/ECTags.h>
  109. #include "ECFeatures.h"
  110. #include <kopano/ECChannel.h>
  111. #include <kopano/UnixUtil.h>
  112. #include "LMTP.h"
  113. #include <kopano/ecversion.h>
  114. #include <csignal>
  115. #include "SSLUtil.h"
  116. #include "StatsClient.h"
  117. #include <execinfo.h>
  118. using namespace std;
  119. using namespace KC;
  120. using namespace KCHL;
  121. static StatsClient *sc = NULL;
  122. enum _dt {
  123. DM_STORE=0,
  124. DM_JUNK,
  125. DM_PUBLIC
  126. };
  127. typedef _dt delivery_mode;
  128. class DeliveryArgs {
  129. public:
  130. DeliveryArgs(void)
  131. {
  132. imopt_default_delivery_options(&sDeliveryOpts);
  133. }
  134. ~DeliveryArgs()
  135. {
  136. delete lpChannel;
  137. }
  138. /* Channel for communication from MTA */
  139. ECChannel *lpChannel = nullptr;
  140. /* Connection path to storage server */
  141. std::string strPath;
  142. /* Path to autorespond handler */
  143. std::string strAutorespond;
  144. /* Options for delivery into special subfolder */
  145. bool bCreateFolder = false;
  146. std::wstring strDeliveryFolder;
  147. WCHAR szPathSeperator = '\\';
  148. /* Delivery options */
  149. delivery_mode ulDeliveryMode = DM_STORE;
  150. delivery_options sDeliveryOpts;
  151. /* Generate notifications regarding the new email */
  152. bool bNewmailNotify = false;
  153. /* Username is email address, resolve it to get username */
  154. bool bResolveAddress = false;
  155. };
  156. /**
  157. * ECRecipient contains an email address from LMTP, or from
  158. * commandline an email address or username.
  159. */
  160. class ECRecipient {
  161. public:
  162. ECRecipient(const std::wstring &wstrName)
  163. {
  164. /* strRCPT much match recipient string from LMTP caller */
  165. wstrRCPT = wstrName;
  166. vwstrRecipients.push_back(wstrName);
  167. sEntryId.cb = 0;
  168. sEntryId.lpb = NULL;
  169. sSearchKey.cb = 0;
  170. sSearchKey.lpb = NULL;
  171. }
  172. ~ECRecipient()
  173. {
  174. MAPIFreeBuffer(sEntryId.lpb);
  175. MAPIFreeBuffer(sSearchKey.lpb);
  176. }
  177. void combine(ECRecipient *lpRecip) {
  178. vwstrRecipients.push_back(lpRecip->wstrRCPT);
  179. }
  180. // sort recipients on imap data flag, then on username so find() for combine() works correctly.
  181. bool operator <(const ECRecipient &r) const {
  182. if (this->bHasIMAP == r.bHasIMAP)
  183. return this->wstrUsername < r.wstrUsername;
  184. else
  185. return this->bHasIMAP && !r.bHasIMAP;
  186. }
  187. ULONG ulResolveFlags = MAPI_UNRESOLVED;
  188. /* Information from LMTP caller */
  189. std::wstring wstrRCPT;
  190. std::vector<std::wstring> vwstrRecipients;
  191. /* User properties */
  192. std::wstring wstrUsername;
  193. std::wstring wstrFullname;
  194. std::wstring wstrCompany;
  195. std::wstring wstrEmail;
  196. std::wstring wstrServerDisplayName;
  197. std::wstring wstrDeliveryStatus;
  198. ULONG ulDisplayType = 0;
  199. ULONG ulAdminLevel = 0;
  200. std::string strAddrType;
  201. std::string strSMTP;
  202. SBinary sEntryId;
  203. SBinary sSearchKey;
  204. bool bHasIMAP = false;
  205. };
  206. class kc_icase_hash {
  207. public:
  208. size_t operator()(const std::string &i) const
  209. {
  210. return std::hash<std::string>()(strToLower(i));
  211. }
  212. };
  213. class kc_icase_equal {
  214. public:
  215. bool operator()(const std::string &a, const std::string &b) const
  216. {
  217. return strcasecmp(a.c_str(), b.c_str()) == 0;
  218. }
  219. };
  220. //Global variables
  221. static bool g_bQuit = false;
  222. static bool g_bTempfail = true; // Most errors are tempfails
  223. static unsigned int g_nLMTPThreads = 0;
  224. ECLogger *g_lpLogger = NULL;
  225. ECConfig *g_lpConfig = NULL;
  226. class sortRecipients {
  227. public:
  228. bool operator()(const ECRecipient *left, const ECRecipient *right) const
  229. {
  230. return *left < *right;
  231. }
  232. };
  233. typedef std::set<ECRecipient *, sortRecipients> recipients_t;
  234. // we group by server to correctly single-instance the email data on each server
  235. typedef std::map<std::wstring, recipients_t, wcscasecmp_comparison> serverrecipients_t;
  236. // then we group by company to minimize re-opening the addressbook
  237. typedef std::map<std::wstring, serverrecipients_t, wcscasecmp_comparison> companyrecipients_t;
  238. static void sigterm(int)
  239. {
  240. g_bQuit = true;
  241. }
  242. static void sighup(int sig)
  243. {
  244. if (g_lpConfig != nullptr && !g_lpConfig->ReloadSettings() &&
  245. g_lpLogger != nullptr)
  246. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to reload configuration file, continuing with current settings.");
  247. if (g_lpLogger) {
  248. if (g_lpConfig) {
  249. const char *ll = g_lpConfig->GetSetting("log_level");
  250. int new_ll = ll ? atoi(ll) : EC_LOGLEVEL_WARNING;
  251. g_lpLogger->SetLoglevel(new_ll);
  252. }
  253. g_lpLogger->Reset();
  254. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Log connection was reset");
  255. }
  256. }
  257. static void sigchld(int)
  258. {
  259. int stat;
  260. while (waitpid (-1, &stat, WNOHANG) > 0)
  261. --g_nLMTPThreads;
  262. }
  263. // Look for segmentation faults
  264. static void sigsegv(int signr, siginfo_t *si, void *uc)
  265. {
  266. generic_sigsegv_handler(g_lpLogger, "Spooler/DAgent",
  267. PROJECT_VERSION_SPOOLER_STR, signr, si, uc);
  268. }
  269. /**
  270. * Check if the message should be processed with the autoaccepter
  271. *
  272. * This function returns TRUE if the message passed is a meeting request or meeting cancellation AND
  273. * the store being delivered in is marked for auto-accepting of meeting requests.
  274. *
  275. * @param lpStore Store that the message will be delivered in
  276. * @param lpMessage Message that will be delivered
  277. * @return TRUE if the message needs to be autoresponded
  278. */
  279. static bool FNeedsAutoAccept(IMsgStore *lpStore, LPMESSAGE lpMessage)
  280. {
  281. HRESULT hr = hrSuccess;
  282. static constexpr const SizedSPropTagArray(2, sptaProps) =
  283. {2, {PR_RESPONSE_REQUESTED, PR_MESSAGE_CLASS}};
  284. memory_ptr<SPropValue> lpProps;
  285. ULONG cValues = 0;
  286. bool bAutoAccept = false, bDeclineConflict = false, bDeclineRecurring = false;
  287. hr = lpMessage->GetProps(sptaProps, 0, &cValues, &~lpProps);
  288. if (FAILED(hr)) {
  289. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FNeedsAutoAccept(): GetProps failed %x", hr);
  290. return false; /* hr */
  291. }
  292. if (PROP_TYPE(lpProps[1].ulPropTag) == PT_ERROR)
  293. return false; /* MAPI_E_NOT_FOUND */
  294. if (wcscasecmp(lpProps[1].Value.lpszW, L"IPM.Schedule.Meeting.Request") != 0 && wcscasecmp(lpProps[1].Value.lpszW, L"IPM.Schedule.Meeting.Canceled") != 0)
  295. return false; /* MAPI_E_NOT_FOUND */
  296. if ((PROP_TYPE(lpProps[0].ulPropTag) == PT_ERROR || !lpProps[0].Value.b) &&
  297. wcscasecmp(lpProps[1].Value.lpszW, L"IPM.Schedule.Meeting.Request") == 0)
  298. // PR_RESPONSE_REQUESTED must be true for requests to start the auto accepter
  299. return false; /* MAPI_E_NOT_FOUND */
  300. hr = GetAutoAcceptSettings(lpStore, &bAutoAccept, &bDeclineConflict, &bDeclineRecurring);
  301. if (hr != hrSuccess) {
  302. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FNeedsAutoAccept(): GetAutoAcceptSettings failed %x", hr);
  303. return false; /* hr */
  304. }
  305. if (!bAutoAccept)
  306. return false; /* MAPI_E_NOT_FOUND */
  307. return true;
  308. }
  309. /**
  310. * Checks whether the message needs auto-processing
  311. */
  312. static bool FNeedsAutoProcessing(IMessage *lpMessage)
  313. {
  314. HRESULT hr = hrSuccess;
  315. static constexpr const SizedSPropTagArray(1, sptaProps) = {1, {PR_MESSAGE_CLASS}};
  316. memory_ptr<SPropValue> lpProps;
  317. ULONG cValues = 0;
  318. hr = lpMessage->GetProps(sptaProps, 0, &cValues, &~lpProps);
  319. if (hr != hrSuccess) {
  320. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FNeedsAutoProcessing(): GetProps failed %x", hr);
  321. return false; /* hr */
  322. }
  323. if (wcsncasecmp(lpProps[0].Value.lpszW, L"IPM.Schedule.Meeting.", wcslen(L"IPM.Schedule.Meeting.")) != 0)
  324. return false; /* MAPI_E_NOT_FOUND */
  325. return true;
  326. }
  327. /**
  328. * Auto-respond to the passed message
  329. *
  330. * This function starts the external autoresponder. Since the external autoresponder needs to access a message,
  331. * we first copy (save) the message into the root folder of the store, then run the script on that message, and remove
  332. * the message afterwards (if the item was accepted, then it will have been moved to the calendar, which will cause
  333. * the delete to fail, but that's expected).
  334. *
  335. * @param lpRecip Recipient for whom lpMessage is being delivered
  336. * @param lpStore Store in which lpMessage is being delivered
  337. * @param lpMessage Message being delivered, should be a meeting request
  338. *
  339. * @return result
  340. */
  341. static HRESULT HrAutoAccept(ECRecipient *lpRecip, IMsgStore *lpStore,
  342. IMessage *lpMessage)
  343. {
  344. HRESULT hr = hrSuccess;
  345. object_ptr<IMAPIFolder> lpRootFolder;
  346. object_ptr<IMessage> lpMessageCopy;
  347. const char *autoresponder = g_lpConfig->GetSetting("mr_autoaccepter");
  348. std::string strEntryID, strCmdLine;
  349. memory_ptr<SPropValue> lpEntryID;
  350. ULONG ulType = 0;
  351. ENTRYLIST sEntryList;
  352. sc -> countInc("DAgent", "AutoAccept");
  353. // Our autoaccepter is an external script. This means that the message it is working on must be
  354. // saved so that it can find the message to open. Since we can't save the passed lpMessage (it
  355. // must be processed by the rules engine first), we make a copy, and let the autoaccept script
  356. // work on the copy.
  357. hr = lpStore->OpenEntry(0, nullptr, nullptr, MAPI_MODIFY, &ulType, &~lpRootFolder);
  358. if(hr != hrSuccess) {
  359. ec_log_err("HrAutoAccept(): OpenEntry failed %x", hr);
  360. return hr;
  361. }
  362. hr = lpRootFolder->CreateMessage(nullptr, 0, &~lpMessageCopy);
  363. if(hr != hrSuccess) {
  364. ec_log_err("HrAutoAccept(): CreateMessage failed %x", hr);
  365. return hr;
  366. }
  367. hr = lpMessage->CopyTo(0, NULL, NULL, 0, NULL, &IID_IMessage, (LPVOID)lpMessageCopy, 0, NULL);
  368. if(hr != hrSuccess) {
  369. ec_log_err("HrAutoAccept(): CopyTo failed %x", hr);
  370. return hr;
  371. }
  372. hr = lpMessageCopy->SaveChanges(0);
  373. if(hr != hrSuccess) {
  374. ec_log_err("HrAutoAccept(): SaveChanges failed %x", hr);
  375. return hr;
  376. }
  377. hr = HrGetOneProp(lpMessageCopy, PR_ENTRYID, &~lpEntryID);
  378. if (hr != hrSuccess) {
  379. ec_log_err("HrAutoAccept(): HrGetOneProp failed %x", hr);
  380. return hr;
  381. }
  382. strEntryID = bin2hex(lpEntryID->Value.bin.cb, lpEntryID->Value.bin.lpb);
  383. // We cannot rely on the 'current locale' to be able to represent the username in wstrUsername. We therefore
  384. // force UTF-8 output on the username. This means that the autoaccept script must also interpret the username
  385. // in UTF-8, *not* in the current locale.
  386. strCmdLine = (std::string)autoresponder + " \"" + convert_to<string>("UTF-8", lpRecip->wstrUsername, rawsize(lpRecip->wstrUsername), CHARSET_WCHAR) + "\" \"" + g_lpConfig->GetSettingsPath() + "\" \"" + strEntryID + "\"";
  387. ec_log_debug("Starting autoaccept with command line %s", strCmdLine.c_str());
  388. if (!unix_system(autoresponder, strCmdLine.c_str(), const_cast<const char **>(environ))) {
  389. hr = MAPI_E_CALL_FAILED;
  390. ec_log_err("HrAutoAccept(): invoking autoaccept script failed %x", hr);
  391. }
  392. // Delete the copy, irrespective of the outcome of the script.
  393. sEntryList.cValues = 1;
  394. sEntryList.lpbin = &lpEntryID->Value.bin;
  395. lpRootFolder->DeleteMessages(&sEntryList, 0, NULL, 0);
  396. // ignore error during delete; the autoaccept script may have already (re)moved the message
  397. return hr;
  398. }
  399. /**
  400. * Auto-process the passed message
  401. *
  402. * @param lpRecip Recipient for whom lpMessage is being delivered
  403. * @param lpStore Store in which lpMessage is being delivered
  404. * @param lpMessage Message being delivered, should be a meeting request
  405. *
  406. * @return result
  407. */
  408. static HRESULT HrAutoProcess(ECRecipient *lpRecip, IMsgStore *lpStore,
  409. IMessage *lpMessage)
  410. {
  411. HRESULT hr = hrSuccess;
  412. object_ptr<IMAPIFolder> lpRootFolder;
  413. object_ptr<IMessage> lpMessageCopy;
  414. const char *autoprocessor = g_lpConfig->GetSetting("mr_autoprocessor");
  415. std::string strEntryID, strCmdLine;
  416. memory_ptr<SPropValue> lpEntryID;
  417. ULONG ulType = 0;
  418. ENTRYLIST sEntryList;
  419. sc -> countInc("DAgent", "AutoProcess");
  420. // Pass a copy to the external script
  421. hr = lpStore->OpenEntry(0, nullptr, nullptr, MAPI_MODIFY, &ulType, &~lpRootFolder);
  422. if(hr != hrSuccess) {
  423. ec_log_err("HrAutoProcess(): OpenEntry failed %x", hr);
  424. return hr;
  425. }
  426. hr = lpRootFolder->CreateMessage(nullptr, 0, &~lpMessageCopy);
  427. if(hr != hrSuccess) {
  428. ec_log_err("HrAutoProcess(): CreateMessage failed %x", hr);
  429. return hr;
  430. }
  431. hr = lpMessage->CopyTo(0, NULL, NULL, 0, NULL, &IID_IMessage, (LPVOID)lpMessageCopy, 0, NULL);
  432. if(hr != hrSuccess) {
  433. ec_log_err("HrAutoProcess(): CopyTo failed %x", hr);
  434. return hr;
  435. }
  436. hr = lpMessageCopy->SaveChanges(0);
  437. if(hr != hrSuccess) {
  438. ec_log_err("HrAutoProcess(): SaveChanges failed %x", hr);
  439. return hr;
  440. }
  441. hr = HrGetOneProp(lpMessageCopy, PR_ENTRYID, &~lpEntryID);
  442. if (hr != hrSuccess) {
  443. ec_log_err("HrAutoProcess(): HrGetOneProp failed %x", hr);
  444. return hr;
  445. }
  446. strEntryID = bin2hex(lpEntryID->Value.bin.cb, lpEntryID->Value.bin.lpb);
  447. // We cannot rely on the 'current locale' to be able to represent the username in wstrUsername. We therefore
  448. // force UTF-8 output on the username. This means that the autoaccept script must also interpret the username
  449. // in UTF-8, *not* in the current locale.
  450. strCmdLine = (std::string)autoprocessor + " \"" + convert_to<string>("UTF-8", lpRecip->wstrUsername, rawsize(lpRecip->wstrUsername), CHARSET_WCHAR) + "\" \"" + g_lpConfig->GetSettingsPath() + "\" \"" + strEntryID + "\"";
  451. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Starting autoaccept with command line %s", strCmdLine.c_str());
  452. if (!unix_system(autoprocessor, strCmdLine.c_str(), const_cast<const char **>(environ)))
  453. hr = MAPI_E_CALL_FAILED;
  454. // Delete the copy, irrespective of the outcome of the script.
  455. sEntryList.cValues = 1;
  456. sEntryList.lpbin = &lpEntryID->Value.bin;
  457. lpRootFolder->DeleteMessages(&sEntryList, 0, NULL, 0);
  458. // ignore error during delete; the autoaccept script may have already (re)moved the message
  459. return hr;
  460. }
  461. static bool kc_recip_in_list(const char *s, const char *recip)
  462. {
  463. auto l = tokenize(s, ' ', true);
  464. return std::find(l.cbegin(), l.cend(), std::string(recip)) != l.cend();
  465. }
  466. /**
  467. * Save copy of the raw message
  468. *
  469. * @param[in] fp File pointer to the email data
  470. * @param[in] lpRecipient Pointer to a recipient name
  471. */
  472. static void SaveRawMessage(FILE *fp, const char *lpRecipient)
  473. {
  474. if (!g_lpConfig || !g_lpLogger || !fp || !lpRecipient)
  475. return;
  476. std::string strFileName = g_lpConfig->GetSetting("log_raw_message_path");
  477. auto rawmsg = g_lpConfig->GetSetting("log_raw_message");
  478. /*
  479. * Either rawmsg contains:
  480. * - no: do not save messages (default)
  481. * - all|yes: save for all users (yes for backward compatibility)
  482. * - space-separated user list
  483. */
  484. if (parseBool(rawmsg) && (strcasecmp(rawmsg, "all") == 0 ||
  485. strcasecmp(rawmsg, "yes") == 0 ||
  486. kc_recip_in_list(rawmsg, lpRecipient))) {
  487. char szBuff[64];
  488. tm tmResult;
  489. time_t now = time(NULL);
  490. gmtime_safe(&now, &tmResult);
  491. // @todo fix windows path!
  492. if (strFileName.empty()) {
  493. g_lpLogger->Log(EC_LOGLEVEL_FATAL, "Unable to save raw message. Wrong configuration: field 'log_raw_message_path' is empty.");
  494. return;
  495. }
  496. if (strFileName[strFileName.size()-1] != '/')
  497. strFileName += '/';
  498. strFileName += lpRecipient;
  499. sprintf(szBuff, "_%04d%02d%02d%02d%02d%02d_%08x.eml", tmResult.tm_year+1900, tmResult.tm_mon+1, tmResult.tm_mday, tmResult.tm_hour, tmResult.tm_min, tmResult.tm_sec, rand_mt());
  500. strFileName += szBuff;
  501. if (DuplicateFile(fp, strFileName))
  502. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "Raw message saved to '%s'", strFileName.c_str());
  503. }
  504. }
  505. /**
  506. * Opens the default addressbook container on the given addressbook.
  507. *
  508. * @param[in] lpAdrBook The IAddrBook interface
  509. * @param[out] lppAddrDir The default addressbook container.
  510. * @return MAPI error code
  511. */
  512. static HRESULT OpenResolveAddrFolder(LPADRBOOK lpAdrBook,
  513. IABContainer **lppAddrDir)
  514. {
  515. HRESULT hr = hrSuccess;
  516. memory_ptr<ENTRYID> lpEntryId;
  517. ULONG cbEntryId = 0;
  518. ULONG ulObj = 0;
  519. if (lpAdrBook == nullptr || lppAddrDir == nullptr)
  520. return MAPI_E_INVALID_PARAMETER;
  521. hr = lpAdrBook->GetDefaultDir(&cbEntryId, &~lpEntryId);
  522. if (hr != hrSuccess) {
  523. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to find default resolve directory: %s (%x)",
  524. GetMAPIErrorMessage(hr), hr);
  525. return hr;
  526. }
  527. hr = lpAdrBook->OpenEntry(cbEntryId, lpEntryId, NULL, 0, &ulObj, (LPUNKNOWN*)lppAddrDir);
  528. if (hr != hrSuccess) {
  529. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open default resolve directory: %s (%x)",
  530. GetMAPIErrorMessage(hr), hr);
  531. return hr;
  532. }
  533. return hrSuccess;
  534. }
  535. /**
  536. * Opens the addressbook on the given session, and optionally opens
  537. * the default addressbook container of the addressbook.
  538. *
  539. * @param[in] lpSession The IMAPISession interface of the logged in user.
  540. * @param[out] lpAdrBook The Global Addressbook.
  541. * @param[out] lppAddrDir The default addressbook container, may be NULL if not wanted.
  542. * @return MAPI error code.
  543. */
  544. static HRESULT OpenResolveAddrFolder(IMAPISession *lpSession,
  545. LPADRBOOK *lppAdrBook, IABContainer **lppAddrDir)
  546. {
  547. if (lpSession == NULL || lppAdrBook == NULL)
  548. return MAPI_E_INVALID_PARAMETER;
  549. HRESULT hr = lpSession->OpenAddressBook(0, NULL, 0, lppAdrBook);
  550. if(hr != hrSuccess) {
  551. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open addressbook: %s (%x)",
  552. GetMAPIErrorMessage(hr), hr);
  553. return hr;
  554. }
  555. if (lppAddrDir) {
  556. hr = OpenResolveAddrFolder(*lppAdrBook, lppAddrDir);
  557. if(hr != hrSuccess) {
  558. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "OpenResolveAddrFolder() OpenResolveAddrFolder failed %x", hr);
  559. return hr;
  560. }
  561. }
  562. return hrSuccess;
  563. }
  564. /**
  565. * Resolve usernames/email addresses to Kopano users.
  566. *
  567. * @param[in] lpAddrFolder resolve users from this addressbook container
  568. * @param[in,out] lRCPT the list of recipients to resolve in Kopano
  569. *
  570. * @return MAPI Error code
  571. */
  572. static HRESULT ResolveUsers(IABContainer *lpAddrFolder, recipients_t *lRCPT)
  573. {
  574. HRESULT hr = hrSuccess;
  575. adrlist_ptr lpAdrList;
  576. memory_ptr<FlagList> lpFlagList;
  577. static constexpr const SizedSPropTagArray(13, sptaAddress) = {13,
  578. { PR_ENTRYID, PR_DISPLAY_NAME_W, PR_ACCOUNT_W, PR_SMTP_ADDRESS_A,
  579. PR_ADDRTYPE_A, PR_EMAIL_ADDRESS_W, PR_DISPLAY_TYPE, PR_SEARCH_KEY,
  580. PR_EC_COMPANY_NAME_W, PR_EC_HOMESERVER_NAME_W, PR_EC_ADMINISTRATOR,
  581. PR_EC_ENABLED_FEATURES_W, PR_OBJECT_TYPE }
  582. };
  583. // pointers into the row data, non-free
  584. /*
  585. LPSPropValue lpEntryIdProp = NULL;
  586. LPSPropValue lpFullNameProp = NULL;
  587. LPSPropValue lpCompanyProp = NULL;
  588. LPSPropValue lpAccountProp = NULL;
  589. LPSPropValue lpSMTPProp = NULL;
  590. LPSPropValue lpServerProp = NULL;
  591. LPSPropValue lpDisplayProp = NULL;
  592. LPSPropValue lpAdminProp = NULL;
  593. LPSPropValue lpAddrTypeProp = NULL;
  594. LPSPropValue lpEmailProp = NULL;
  595. LPSPropValue lpSearchKeyProp = NULL;
  596. LPSPropValue lpFeatureList = NULL;
  597. LPSPropValue lpObjectProp = NULL;
  598. */
  599. ULONG ulRCPT = lRCPT->size();
  600. hr = MAPIAllocateBuffer(CbNewADRLIST(ulRCPT), &~lpAdrList);
  601. if (hr != hrSuccess) {
  602. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ResolveUsers(): MAPIAllocateBuffer failed(1) %x", hr);
  603. return hr;
  604. }
  605. lpAdrList->cEntries = ulRCPT;
  606. hr = MAPIAllocateBuffer(CbNewFlagList(ulRCPT), &~lpFlagList);
  607. if (hr != hrSuccess) {
  608. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ResolveUsers(): MAPIAllocateBuffer failed(2) %x", hr);
  609. return hr;
  610. }
  611. lpFlagList->cFlags = ulRCPT;
  612. ulRCPT = 0;
  613. for (const auto &recip : *lRCPT) {
  614. lpAdrList->aEntries[ulRCPT].cValues = 1;
  615. hr = MAPIAllocateBuffer(sizeof(SPropValue), (void **) &lpAdrList->aEntries[ulRCPT].rgPropVals);
  616. if (hr != hrSuccess) {
  617. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ResolveUsers(): MAPIAllocateBuffer failed(3) %x", hr);
  618. return hr;
  619. }
  620. /* szName can either be the email address or username, it doesn't really matter */
  621. lpAdrList->aEntries[ulRCPT].rgPropVals[0].ulPropTag = PR_DISPLAY_NAME_W;
  622. lpAdrList->aEntries[ulRCPT].rgPropVals[0].Value.lpszW = const_cast<wchar_t *>(recip->wstrRCPT.c_str());
  623. lpFlagList->ulFlag[ulRCPT] = MAPI_UNRESOLVED;
  624. ++ulRCPT;
  625. }
  626. // MAPI_UNICODE flag here doesn't have any effect, since we give all proptags ourself
  627. hr = lpAddrFolder->ResolveNames(sptaAddress,
  628. MAPI_UNICODE | EMS_AB_ADDRESS_LOOKUP, lpAdrList, lpFlagList);
  629. if (hr != hrSuccess)
  630. return hr;
  631. ulRCPT = 0;
  632. for (const auto &recip : *lRCPT) {
  633. recip->ulResolveFlags = lpFlagList->ulFlag[ulRCPT];
  634. ULONG temp = lpFlagList->ulFlag[ulRCPT];
  635. if (temp != MAPI_RESOLVED) {
  636. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to resolve recipient %ls (%x)", recip->wstrRCPT.c_str(), temp);
  637. continue;
  638. }
  639. /* Yay, resolved the address, get it */
  640. auto lpEntryIdProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_ENTRYID);
  641. auto lpFullNameProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_DISPLAY_NAME_W);
  642. auto lpAccountProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_ACCOUNT_W);
  643. auto lpSMTPProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_SMTP_ADDRESS_A);
  644. auto lpObjectProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_OBJECT_TYPE);
  645. // the only property that is allowed NULL in this list
  646. auto lpDisplayProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_DISPLAY_TYPE);
  647. if(!lpEntryIdProp || !lpFullNameProp || !lpAccountProp || !lpSMTPProp || !lpObjectProp) {
  648. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Not all properties found for %ls", recip->wstrRCPT.c_str());
  649. continue;
  650. }
  651. if (lpObjectProp->Value.ul != MAPI_MAILUSER) {
  652. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Resolved recipient %ls is not a user", recip->wstrRCPT.c_str());
  653. continue;
  654. } else if (lpDisplayProp && lpDisplayProp->Value.ul == DT_REMOTE_MAILUSER) {
  655. // allowed are DT_MAILUSER, DT_ROOM and DT_EQUIPMENT. all other DT_* defines are no MAPI_MAILUSER
  656. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Resolved recipient %ls is a contact address, unable to deliver", recip->wstrRCPT.c_str());
  657. continue;
  658. }
  659. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "Resolved recipient %ls as user %ls", recip->wstrRCPT.c_str(), lpAccountProp->Value.lpszW);
  660. /* The following are allowed to be NULL */
  661. auto lpCompanyProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_EC_COMPANY_NAME_W);
  662. auto lpServerProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_EC_HOMESERVER_NAME_W);
  663. auto lpAdminProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_EC_ADMINISTRATOR);
  664. auto lpAddrTypeProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_ADDRTYPE_A);
  665. auto lpEmailProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_EMAIL_ADDRESS_W);
  666. auto lpSearchKeyProp = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_SEARCH_KEY);
  667. recip->wstrUsername.assign(lpAccountProp->Value.lpszW);
  668. recip->wstrFullname.assign(lpFullNameProp->Value.lpszW);
  669. recip->strSMTP.assign(lpSMTPProp->Value.lpszA);
  670. if (Util::HrCopyBinary(lpEntryIdProp->Value.bin.cb, lpEntryIdProp->Value.bin.lpb, &recip->sEntryId.cb, &recip->sEntryId.lpb) != hrSuccess)
  671. continue;
  672. /* Only when multi-company has been enabled will we have the companyname. */
  673. if (lpCompanyProp)
  674. recip->wstrCompany.assign(lpCompanyProp->Value.lpszW);
  675. /* Only when distributed has been enabled will we have the servername. */
  676. if (lpServerProp)
  677. recip->wstrServerDisplayName.assign(lpServerProp->Value.lpszW);
  678. if (lpDisplayProp)
  679. recip->ulDisplayType = lpDisplayProp->Value.ul;
  680. if (lpAdminProp)
  681. recip->ulAdminLevel = lpAdminProp->Value.ul;
  682. if (lpAddrTypeProp)
  683. recip->strAddrType.assign(lpAddrTypeProp->Value.lpszA);
  684. else
  685. recip->strAddrType.assign("SMTP");
  686. if (lpEmailProp)
  687. recip->wstrEmail.assign(lpEmailProp->Value.lpszW);
  688. if (lpSearchKeyProp) {
  689. if (Util::HrCopyBinary(lpSearchKeyProp->Value.bin.cb, lpSearchKeyProp->Value.bin.lpb, &recip->sSearchKey.cb, &recip->sSearchKey.lpb) != hrSuccess)
  690. continue;
  691. } else {
  692. std::string key = "SMTP:" + recip->strSMTP;
  693. key = strToUpper(key);
  694. recip->sSearchKey.cb = key.size() + 1; // + terminating 0
  695. if (MAPIAllocateBuffer(recip->sSearchKey.cb, reinterpret_cast<void **>(&recip->sSearchKey.lpb)) != hrSuccess) {
  696. ++ulRCPT;
  697. continue;
  698. }
  699. memcpy(recip->sSearchKey.lpb, key.c_str(), recip->sSearchKey.cb);
  700. }
  701. auto lpFeatureList = PCpropFindProp(lpAdrList->aEntries[ulRCPT].rgPropVals, lpAdrList->aEntries[ulRCPT].cValues, PR_EC_ENABLED_FEATURES_W);
  702. recip->bHasIMAP = lpFeatureList && hasFeature(L"imap", lpFeatureList) == hrSuccess;
  703. ++ulRCPT;
  704. }
  705. return hrSuccess;
  706. }
  707. /**
  708. * Resolve a single recipient as Kopano user
  709. *
  710. * @param[in] lpAddrFolder resolve users from this addressbook container
  711. * @param[in,out] lpRecip recipient to resolve in Kopano
  712. *
  713. * @return MAPI Error code
  714. */
  715. static HRESULT ResolveUser(IABContainer *lpAddrFolder, ECRecipient *lpRecip)
  716. {
  717. HRESULT hr = hrSuccess;
  718. recipients_t list;
  719. /* Simple wrapper around ResolveUsers */
  720. list.insert(lpRecip);
  721. hr = ResolveUsers(lpAddrFolder, &list);
  722. if (hr != hrSuccess)
  723. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ResolveUser(): ResolveUsers failed %x", hr);
  724. else if (lpRecip->ulResolveFlags != MAPI_RESOLVED)
  725. hr = MAPI_E_NOT_FOUND;
  726. return hr;
  727. }
  728. /**
  729. * Free a list of recipients
  730. *
  731. * @param[in] lpCompanyRecips list to free memory of, and clear.
  732. *
  733. * @return MAPI Error code
  734. */
  735. static HRESULT FreeServerRecipients(companyrecipients_t *lpCompanyRecips)
  736. {
  737. if (lpCompanyRecips == NULL)
  738. return MAPI_E_INVALID_PARAMETER;
  739. for (const auto &cmp : *lpCompanyRecips)
  740. for (const auto &srv : cmp.second)
  741. for (const auto &rcpt : srv.second)
  742. delete rcpt;
  743. lpCompanyRecips->clear();
  744. return hrSuccess;
  745. }
  746. /**
  747. * Add a recipient to a delivery list, grouped by companies and
  748. * servers. If recipient is added to the container, it will be set to
  749. * NULL so you can't free it anymore. It will be freed when the
  750. * container is freed.
  751. *
  752. * @param[in,out] lpCompanyRecips container to add recipient in
  753. * @param[in,out] lppRecipient Recipient to add to the container
  754. *
  755. * @return MAPI Error code
  756. */
  757. static HRESULT AddServerRecipient(companyrecipients_t *lpCompanyRecips,
  758. ECRecipient **lppRecipient)
  759. {
  760. ECRecipient *lpRecipient = *lppRecipient;
  761. if (lpCompanyRecips == NULL)
  762. return MAPI_E_INVALID_PARAMETER;
  763. // Find or insert
  764. auto iterCMP = lpCompanyRecips->insert(companyrecipients_t::value_type(lpRecipient->wstrCompany, serverrecipients_t())).first;
  765. // Find or insert
  766. auto iterSRV = iterCMP->second.insert(serverrecipients_t::value_type(lpRecipient->wstrServerDisplayName, recipients_t())).first;
  767. // insert into sorted set
  768. auto iterRecip = iterSRV->second.find(lpRecipient);
  769. if (iterRecip == iterSRV->second.cend()) {
  770. iterSRV->second.insert(lpRecipient);
  771. // The recipient is in the list, and no longer belongs to the caller
  772. *lppRecipient = NULL;
  773. } else {
  774. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Combining recipient %ls and %ls, delivering only once", lpRecipient->wstrRCPT.c_str(), (*iterRecip)->wstrUsername.c_str());
  775. (*iterRecip)->combine(lpRecipient);
  776. }
  777. return hrSuccess;
  778. }
  779. /**
  780. * Make a map of recipients grouped by url instead of server name
  781. *
  782. * @param[in] lpSession MAPI admin session
  783. * @param[in] lpServerNameRecips recipients grouped by server name
  784. * @param[in] strDefaultPath default connection url to kopano
  785. * @param[out] lpServerPathRecips recipients grouped by server url
  786. *
  787. * @return MAPI Error code
  788. */
  789. static HRESULT ResolveServerToPath(IMAPISession *lpSession,
  790. const serverrecipients_t *lpServerNameRecips,
  791. const std::string &strDefaultPath, serverrecipients_t *lpServerPathRecips)
  792. {
  793. HRESULT hr = hrSuccess;
  794. object_ptr<IMsgStore> lpAdminStore;
  795. object_ptr<IECServiceAdmin> lpServiceAdmin;
  796. memory_ptr<SPropValue> lpsObject;
  797. memory_ptr<ECSVRNAMELIST> lpSrvNameList;
  798. memory_ptr<ECSERVERLIST> lpSrvList;
  799. if (lpServerNameRecips == nullptr || lpServerPathRecips == nullptr)
  800. return MAPI_E_INVALID_PARAMETER;
  801. /* Single server environment, use default path */
  802. if (lpServerNameRecips->size() == 1 && lpServerNameRecips->begin()->first.empty()) {
  803. lpServerPathRecips->insert(serverrecipients_t::value_type(convert_to<wstring>(strDefaultPath), lpServerNameRecips->begin()->second));
  804. return hrSuccess;
  805. }
  806. hr = HrOpenDefaultStore(lpSession, &~lpAdminStore);
  807. if (hr != hrSuccess) {
  808. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open default store for system account, error code: 0x%08X", hr);
  809. // HrLogon() failed .. try again later
  810. return hr;
  811. }
  812. hr = HrGetOneProp(lpAdminStore, PR_EC_OBJECT, &~lpsObject);
  813. if(hr != hrSuccess) {
  814. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get internal object, error code: 0x%08X", hr);
  815. return hr;
  816. }
  817. // NOTE: object is placed in Value.lpszA, not Value.x
  818. hr = ((IECUnknown *)lpsObject->Value.lpszA)->QueryInterface(IID_IECServiceAdmin, &~lpServiceAdmin);
  819. if(hr != hrSuccess) {
  820. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get service admin, error code: 0x%08X", hr);
  821. return hr;
  822. }
  823. hr = MAPIAllocateBuffer(sizeof(ECSVRNAMELIST), &~lpSrvNameList);
  824. if (hr != hrSuccess) {
  825. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ResolveServerToPath(): MAPIAllocateBuffer failed %x", hr);
  826. return hr;
  827. }
  828. hr = MAPIAllocateMore(sizeof(WCHAR *) * lpServerNameRecips->size(), lpSrvNameList, (LPVOID *)&lpSrvNameList->lpszaServer);
  829. if (hr != hrSuccess) {
  830. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ResolveServerToPath(): MAPIAllocateMore failed(1) %x", hr);
  831. return hr;
  832. }
  833. lpSrvNameList->cServers = 0;
  834. for (const auto &iter : *lpServerNameRecips) {
  835. if (iter.first.empty())
  836. // recipient doesn't have a home server.
  837. // don't try to resolve since that will break the GetServerDetails call
  838. // and thus fail all recipients, not just this one
  839. continue;
  840. hr = MAPIAllocateMore((iter.first.size() + 1) * sizeof(wchar_t),
  841. lpSrvNameList, reinterpret_cast<LPVOID *>(&lpSrvNameList->lpszaServer[lpSrvNameList->cServers]));
  842. if (hr != hrSuccess) {
  843. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ResolveServerToPath(): MAPIAllocateMore failed(2) %x", hr);
  844. return hr;
  845. }
  846. wcscpy(reinterpret_cast<LPWSTR>(lpSrvNameList->lpszaServer[lpSrvNameList->cServers]), iter.first.c_str());
  847. ++lpSrvNameList->cServers;
  848. }
  849. hr = lpServiceAdmin->GetServerDetails(lpSrvNameList, EC_SERVERDETAIL_PREFEREDPATH | MAPI_UNICODE, &~lpSrvList);
  850. if (hr != hrSuccess) {
  851. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ResolveServerToPath(): GetServerDetails failed %x", hr);
  852. return hr;
  853. }
  854. for (ULONG i = 0; i < lpSrvList->cServers; ++i) {
  855. auto iter = lpServerNameRecips->find((LPWSTR)lpSrvList->lpsaServer[i].lpszName);
  856. if (iter == lpServerNameRecips->cend()) {
  857. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Server '%s' not found", (char*)lpSrvList->lpsaServer[i].lpszName);
  858. return MAPI_E_NOT_FOUND;
  859. }
  860. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "%d recipient(s) on server '%ls' (url %ls)", (int)iter->second.size(),
  861. lpSrvList->lpsaServer[i].lpszName, lpSrvList->lpsaServer[i].lpszPreferedPath);
  862. lpServerPathRecips->insert(serverrecipients_t::value_type((LPWSTR)lpSrvList->lpsaServer[i].lpszPreferedPath, iter->second));
  863. }
  864. return hrSuccess;
  865. }
  866. /**
  867. * For a given recipient, open its store, inbox and delivery folder.
  868. *
  869. * @param[in] lpSession MAPI Admin session
  870. * @param[in] lpAdminStore Store of the admin
  871. * @param[in] lpRecip Resolved Kopano recipient to open folders for
  872. * @param[in] lpArgs Use these delivery options to open correct folders etc.
  873. * @param[out] lppStore Store of the recipient
  874. * @param[out] lppInbox Inbox of the recipient
  875. * @param[out] lppFolder Delivery folder of the recipient
  876. *
  877. * @return MAPI Error code
  878. */
  879. static HRESULT HrGetDeliveryStoreAndFolder(IMAPISession *lpSession,
  880. IMsgStore *lpAdminStore, ECRecipient *lpRecip, DeliveryArgs *lpArgs,
  881. LPMDB *lppStore, IMAPIFolder **lppInbox, IMAPIFolder **lppFolder)
  882. {
  883. HRESULT hr = hrSuccess;
  884. IMAPIFolder *lpDeliveryFolder;
  885. LPMDB lpDeliveryStore = NULL;
  886. object_ptr<IMsgStore> lpUserStore, lpPublicStore;
  887. object_ptr<IMAPIFolder> lpInbox, lpSubFolder, lpJunkFolder;
  888. object_ptr<IExchangeManageStore> lpIEMS;
  889. memory_ptr<SPropValue> lpJunkProp, lpWritePerms;
  890. ULONG cbUserStoreEntryId = 0;
  891. memory_ptr<ENTRYID> lpUserStoreEntryId, lpEntryId;
  892. ULONG cbEntryId = 0;
  893. ULONG ulObjType = 0;
  894. std::wstring strDeliveryFolder = lpArgs->strDeliveryFolder;
  895. bool bPublicStore = false;
  896. hr = lpAdminStore->QueryInterface(IID_IExchangeManageStore, &~lpIEMS);
  897. if (hr != hrSuccess) {
  898. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrGetDeliveryStoreAndFolder(): QueryInterface failed %x", hr);
  899. return hr;
  900. }
  901. hr = lpIEMS->CreateStoreEntryID((LPTSTR)L"", (LPTSTR)lpRecip->wstrUsername.c_str(), MAPI_UNICODE | OPENSTORE_HOME_LOGON, &cbUserStoreEntryId, &~lpUserStoreEntryId);
  902. if (hr != hrSuccess) {
  903. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrGetDeliveryStoreAndFolder(): CreateStoreEntry failed %x", hr);
  904. return hr;
  905. }
  906. hr = lpSession->OpenMsgStore(0, cbUserStoreEntryId, lpUserStoreEntryId, nullptr, MDB_WRITE | MDB_NO_DIALOG | MDB_NO_MAIL | MDB_TEMPORARY, &~lpUserStore);
  907. if (hr != hrSuccess) {
  908. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrGetDeliveryStoreAndFolder(): OpenMsgStore failed %x", hr);
  909. return hr;
  910. }
  911. hr = lpUserStore->GetReceiveFolder((LPTSTR)"IPM", 0, &cbEntryId, &~lpEntryId, NULL);
  912. if (hr != hrSuccess) {
  913. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to resolve incoming folder, error code: 0x%08X", hr);
  914. return hr;
  915. }
  916. // Open the inbox
  917. hr = lpUserStore->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpInbox);
  918. if (hr != hrSuccess || ulObjType != MAPI_FOLDER) {
  919. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open inbox folder, error code: 0x%08X", hr);
  920. return MAPI_E_NOT_FOUND;
  921. }
  922. // set default delivery to inbox, and default entryid for notify
  923. lpDeliveryFolder = lpInbox;
  924. lpDeliveryStore = lpUserStore;
  925. switch (lpArgs->ulDeliveryMode) {
  926. case DM_STORE:
  927. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Mail will be delivered in Inbox");
  928. sc -> countInc("DAgent", "deliver_inbox");
  929. break;
  930. case DM_JUNK:
  931. sc -> countInc("DAgent", "deliver_junk");
  932. hr = HrGetOneProp(lpInbox, PR_ADDITIONAL_REN_ENTRYIDS, &~lpJunkProp);
  933. if (hr != hrSuccess || lpJunkProp->Value.MVbin.lpbin[4].cb == 0) {
  934. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to resolve junk folder, using normal Inbox: %s (%x)",
  935. GetMAPIErrorMessage(hr), hr);
  936. break;
  937. }
  938. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Mail will be delivered in junkmail folder");
  939. // Open the Junk folder
  940. hr = lpUserStore->OpenEntry(lpJunkProp->Value.MVbin.lpbin[4].cb, reinterpret_cast<ENTRYID *>(lpJunkProp->Value.MVbin.lpbin[4].lpb),
  941. &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpJunkFolder);
  942. if (hr != hrSuccess || ulObjType != MAPI_FOLDER) {
  943. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to open junkmail folder, using normal Inbox: %s (%x)",
  944. GetMAPIErrorMessage(hr), hr);
  945. break;
  946. }
  947. // set new delivery folder
  948. lpDeliveryFolder = lpJunkFolder;
  949. break;
  950. case DM_PUBLIC:
  951. sc -> countInc("DAgent", "deliver_public");
  952. hr = HrOpenECPublicStore(lpSession, &~lpPublicStore);
  953. if (hr != hrSuccess) {
  954. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open public store, error code 0x%08X", hr);
  955. // revert to normal inbox delivery
  956. strDeliveryFolder.clear();
  957. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Mail will be delivered in Inbox");
  958. } else {
  959. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Mail will be delivered in Public store subfolder");
  960. lpDeliveryStore = lpPublicStore;
  961. bPublicStore = true;
  962. }
  963. break;
  964. };
  965. if (!strDeliveryFolder.empty() && lpArgs->ulDeliveryMode != DM_JUNK) {
  966. hr = OpenSubFolder(lpDeliveryStore, strDeliveryFolder.c_str(),
  967. lpArgs->szPathSeperator, bPublicStore,
  968. lpArgs->bCreateFolder, &~lpSubFolder);
  969. if (hr != hrSuccess) {
  970. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Subfolder not found, using normal Inbox. Error code 0x%08X", hr);
  971. // folder not found, use inbox
  972. lpDeliveryFolder = lpInbox;
  973. lpDeliveryStore = lpUserStore;
  974. } else {
  975. lpDeliveryFolder = lpSubFolder;
  976. }
  977. }
  978. // check if we may write in the selected folder
  979. hr = HrGetOneProp(lpDeliveryFolder, PR_ACCESS_LEVEL, &~lpWritePerms);
  980. if (FAILED(hr)) {
  981. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to read folder properties, error code: 0x%08X", hr);
  982. return hr;
  983. }
  984. if ((lpWritePerms->Value.ul & MAPI_MODIFY) == 0) {
  985. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "No write access in folder, using regular inbox");
  986. lpDeliveryStore = lpUserStore;
  987. lpDeliveryFolder = lpInbox;
  988. }
  989. lpDeliveryStore->AddRef();
  990. *lppStore = lpDeliveryStore;
  991. lpInbox->AddRef();
  992. *lppInbox = lpInbox;
  993. lpDeliveryFolder->AddRef();
  994. *lppFolder = lpDeliveryFolder;
  995. return hrSuccess;
  996. }
  997. /**
  998. * Make the message a fallback message.
  999. *
  1000. * @param[in,out] lpMessage Message to place fallback data in
  1001. * @param[in] msg original rfc2822 received message
  1002. *
  1003. * @return MAPI Error code
  1004. */
  1005. static HRESULT FallbackDelivery(LPMESSAGE lpMessage, const string &msg)
  1006. {
  1007. HRESULT hr;
  1008. memory_ptr<SPropValue> lpPropValue, lpAttPropValue;
  1009. unsigned int ulPropPos;
  1010. FILETIME ft;
  1011. object_ptr<IAttach> lpAttach;
  1012. ULONG ulAttachNum;
  1013. object_ptr<IStream> lpStream;
  1014. unsigned int ulAttPropPos;
  1015. string newbody;
  1016. sc -> countInc("DAgent", "FallbackDelivery");
  1017. // set props
  1018. hr = MAPIAllocateBuffer(sizeof(SPropValue) * 8, &~lpPropValue);
  1019. if (hr != hrSuccess) {
  1020. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FallbackDelivery(): MAPIAllocateBuffer failed %x", hr);
  1021. return hr;
  1022. }
  1023. ulPropPos = 0;
  1024. // Subject
  1025. lpPropValue[ulPropPos].ulPropTag = PR_SUBJECT_W;
  1026. lpPropValue[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"Fallback delivery");
  1027. // Message flags
  1028. lpPropValue[ulPropPos].ulPropTag = PR_MESSAGE_FLAGS;
  1029. lpPropValue[ulPropPos++].Value.ul = 0;
  1030. // Message class
  1031. lpPropValue[ulPropPos].ulPropTag = PR_MESSAGE_CLASS_W;
  1032. lpPropValue[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"IPM.Note");
  1033. GetSystemTimeAsFileTime(&ft);
  1034. // Submit time
  1035. lpPropValue[ulPropPos].ulPropTag = PR_CLIENT_SUBMIT_TIME;
  1036. lpPropValue[ulPropPos++].Value.ft = ft;
  1037. // Delivery time
  1038. lpPropValue[ulPropPos].ulPropTag = PR_MESSAGE_DELIVERY_TIME;
  1039. lpPropValue[ulPropPos++].Value.ft = ft;
  1040. newbody = "An e-mail sent to you could not be delivered correctly.\n\n";
  1041. newbody += "The original message is attached to this e-mail (the one you're reading right now).\n";
  1042. lpPropValue[ulPropPos].ulPropTag = PR_BODY_A;
  1043. lpPropValue[ulPropPos++].Value.lpszA = (char*)newbody.c_str();
  1044. // Add the original message into the errorMessage
  1045. hr = lpMessage->CreateAttach(nullptr, 0, &ulAttachNum, &~lpAttach);
  1046. if (hr != hrSuccess) {
  1047. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to create attachment, error code: 0x%08X", hr);
  1048. return hr;
  1049. }
  1050. hr = lpAttach->OpenProperty(PR_ATTACH_DATA_BIN, &IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_CREATE | MAPI_MODIFY, &~lpStream);
  1051. if (hr != hrSuccess) {
  1052. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FallbackDelivery(): lpAttach->OpenProperty failed %x", hr);
  1053. return hr;
  1054. }
  1055. hr = lpStream->Write(msg.c_str(), msg.size(), NULL);
  1056. if (hr != hrSuccess) {
  1057. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FallbackDelivery(): lpStream->Write failed %x", hr);
  1058. return hr;
  1059. }
  1060. hr = lpStream->Commit(0);
  1061. if (hr != hrSuccess) {
  1062. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FallbackDelivery(): lpStream->Commit failed %x", hr);
  1063. return hr;
  1064. }
  1065. // Add attachment properties
  1066. hr = MAPIAllocateBuffer(sizeof(SPropValue) * 4, &~lpAttPropValue);
  1067. if (hr != hrSuccess) {
  1068. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FallbackDelivery(): MAPIAllocateBuffer failed %x", hr);
  1069. return hr;
  1070. }
  1071. ulAttPropPos = 0;
  1072. // Attach method .. ?
  1073. lpAttPropValue[ulAttPropPos].ulPropTag = PR_ATTACH_METHOD;
  1074. lpAttPropValue[ulAttPropPos++].Value.ul = ATTACH_BY_VALUE;
  1075. lpAttPropValue[ulAttPropPos].ulPropTag = PR_ATTACH_LONG_FILENAME_W;
  1076. lpAttPropValue[ulAttPropPos++].Value.lpszW = const_cast<wchar_t *>(L"original.eml");
  1077. lpAttPropValue[ulAttPropPos].ulPropTag = PR_ATTACH_FILENAME_W;
  1078. lpAttPropValue[ulAttPropPos++].Value.lpszW = const_cast<wchar_t *>(L"original.eml");
  1079. lpAttPropValue[ulAttPropPos].ulPropTag = PR_ATTACH_CONTENT_ID_W;
  1080. lpAttPropValue[ulAttPropPos++].Value.lpszW = const_cast<wchar_t *>(L"dagent-001@localhost");
  1081. // Add attachment properties
  1082. hr = lpAttach->SetProps(ulAttPropPos, lpAttPropValue, NULL);
  1083. if (hr != hrSuccess) {
  1084. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FallbackDelivery(): SetProps failed(1) %x", hr);
  1085. return hr;
  1086. }
  1087. hr = lpAttach->SaveChanges(0);
  1088. if (hr != hrSuccess) {
  1089. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FallbackDelivery(): SaveChanges failed %x", hr);
  1090. return hr;
  1091. }
  1092. // Add message properties
  1093. hr = lpMessage->SetProps(ulPropPos, lpPropValue, NULL);
  1094. if (hr != hrSuccess) {
  1095. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FallbackDelivery(): SetProps failed(2) %x", hr);
  1096. return hr;
  1097. }
  1098. hr = lpMessage->SaveChanges(KEEP_OPEN_READWRITE);
  1099. if (hr != hrSuccess)
  1100. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "FallbackDelivery(): lpMessage->SaveChanges failed %x", hr);
  1101. return hr;
  1102. }
  1103. /**
  1104. * Write into the given fd, and if that fails log an error.
  1105. *
  1106. * @param[in] fd file descriptor to write to
  1107. * @param[in] buffer buffer to write
  1108. * @param[in] len length of buffer to write
  1109. * @param[in] wrap optional wrapping, inserts a \r\n at the point of the wrapping point
  1110. *
  1111. * @return MAPI Error code
  1112. */
  1113. static HRESULT WriteOrLogError(int fd, const char *buffer, size_t len,
  1114. size_t wrap = 0)
  1115. {
  1116. if (!wrap)
  1117. wrap = len;
  1118. while (len > 0) {
  1119. ssize_t n = min(len, wrap);
  1120. if (write_retry(fd, buffer, n) != n) {
  1121. g_lpLogger->Log(EC_LOGLEVEL_ERROR,
  1122. "Write error to temp file for out of office "
  1123. "mail: %s", strerror(errno));
  1124. return MAPI_E_CALL_FAILED;
  1125. }
  1126. buffer += n;
  1127. len -= n;
  1128. if (len > 0 && write_retry(fd, "\r\n", 2) != 2) {
  1129. g_lpLogger->Log(EC_LOGLEVEL_ERROR,
  1130. "Write error to temp file for out of office "
  1131. "mail: %s", strerror(errno));
  1132. return MAPI_E_CALL_FAILED;
  1133. }
  1134. }
  1135. return hrSuccess;
  1136. }
  1137. static bool dagent_oof_active(const SPropValue *prop)
  1138. {
  1139. bool a = prop[0].ulPropTag == PR_EC_OUTOFOFFICE && prop[0].Value.b;
  1140. if (!a)
  1141. return false;
  1142. time_t ts, now = time(nullptr);
  1143. if (prop[3].ulPropTag == PR_EC_OUTOFOFFICE_FROM) {
  1144. FileTimeToUnixTime(prop[3].Value.ft, &ts);
  1145. a &= ts <= now;
  1146. }
  1147. if (prop[4].ulPropTag == PR_EC_OUTOFOFFICE_UNTIL) {
  1148. FileTimeToUnixTime(prop[4].Value.ft, &ts);
  1149. a &= now <= ts;
  1150. }
  1151. return a;
  1152. }
  1153. /**
  1154. * Contains all the exact-match header names that will inhibit autoreplies.
  1155. */
  1156. static const std::unordered_set<std::string, kc_icase_hash, kc_icase_equal> kc_stopreply_hdr = {
  1157. /* Kopano - Vacation header already present, do not send vacation reply. */
  1158. "X-Kopano-Vacation",
  1159. /* RFC 3834 - Precedence: list/bulk/junk, do not reply to these mails. */
  1160. "Auto-Submitted",
  1161. "Precedence",
  1162. /* RFC 2919 */
  1163. "List-Id",
  1164. /* RFC 2369 */
  1165. "List-Help",
  1166. "List-Subscribe",
  1167. "List-Unsubscribe",
  1168. "List-Post",
  1169. "List-Owner",
  1170. "List-Archive",
  1171. };
  1172. /* A list of prefix searches for entire header-value lines */
  1173. static const std::unordered_set<std::string, kc_icase_hash, kc_icase_equal> kc_stopreply_hdr2 = {
  1174. /* From the package "vacation" */
  1175. "X-Spam-Flag: YES",
  1176. /* From openSUSE's vacation package */
  1177. "X-Is-Junk: YES",
  1178. "X-AMAZON",
  1179. "X-LinkedIn",
  1180. };
  1181. /**
  1182. * Determines from a set of lines from internet headers (can be wrapped or
  1183. * not) whether to inhibit autoreplies.
  1184. */
  1185. static bool dagent_avoid_autoreply(const std::vector<std::string> &hl)
  1186. {
  1187. for (const auto &line : hl) {
  1188. if (isspace(line[0]))
  1189. continue;
  1190. size_t pos = line.find_first_of(':');
  1191. if (pos == std::string::npos || pos == 0)
  1192. continue;
  1193. if (kc_stopreply_hdr.find(line.substr(0, pos)) != kc_stopreply_hdr.cend())
  1194. return true;
  1195. for (const auto &elem : kc_stopreply_hdr2)
  1196. if (kc_stopreply_hdr2.find(line.substr(0, elem.size())) != kc_stopreply_hdr2.cend())
  1197. return true;
  1198. }
  1199. return false;
  1200. }
  1201. /**
  1202. * Create an out-of-office mail, and start the script to trigger its
  1203. * optional sending.
  1204. *
  1205. * @param[in] lpAdrBook Addressbook for email address rewrites
  1206. * @param[in] lpMDB Store of the user that triggered the oof email
  1207. * @param[in] lpMessage delivery message that triggered the oof email
  1208. * @param[in] lpRecip delivery recipient sending the oof email from
  1209. * @param[in] strBaseCommand Command to use to start the oof mailer (kopano-autorespond)
  1210. *
  1211. * @return MAPI Error code
  1212. */
  1213. static HRESULT SendOutOfOffice(LPADRBOOK lpAdrBook, LPMDB lpMDB,
  1214. LPMESSAGE lpMessage, ECRecipient *lpRecip,
  1215. const std::string &strBaseCommand)
  1216. {
  1217. HRESULT hr = hrSuccess;
  1218. static constexpr const SizedSPropTagArray(5, sptaStoreProps) = {5, {
  1219. PR_EC_OUTOFOFFICE, PR_EC_OUTOFOFFICE_MSG_W,
  1220. PR_EC_OUTOFOFFICE_SUBJECT_W,
  1221. PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL,
  1222. }};
  1223. static constexpr const SizedSPropTagArray(5, sptaMessageProps) = {5, {
  1224. PR_TRANSPORT_MESSAGE_HEADERS_A, PR_MESSAGE_TO_ME,
  1225. PR_MESSAGE_CC_ME, PR_SUBJECT_W, PR_EC_MESSAGE_BCC_ME,
  1226. }};
  1227. memory_ptr<SPropValue> lpStoreProps, lpMessageProps;
  1228. ULONG cValues;
  1229. const wchar_t *szSubject = L"Out of office";
  1230. char szHeader[PATH_MAX] = {0};
  1231. wchar_t szwHeader[PATH_MAX] = {0};
  1232. char szTemp[PATH_MAX] = {0};
  1233. int fd = -1;
  1234. wstring strFromName, strFromType, strFromEmail, strBody;
  1235. string unquoted, quoted;
  1236. string command = strBaseCommand;
  1237. // Environment
  1238. const char *env[5];
  1239. std::string strToMe;
  1240. std::string strCcMe, strBccMe;
  1241. std::string strTmpFile;
  1242. std::string strTmpFileEnv;
  1243. sc -> countInc("DAgent", "OutOfOffice");
  1244. // @fixme need to stream PR_TRANSPORT_MESSAGE_HEADERS_A and PR_EC_OUTOFOFFICE_MSG_W if they're > 8Kb
  1245. hr = lpMDB->GetProps(sptaStoreProps, 0, &cValues, &~lpStoreProps);
  1246. if (FAILED(hr)) {
  1247. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): GetProps failed(1) %x", hr);
  1248. goto exit;
  1249. }
  1250. hr = hrSuccess;
  1251. // Check for autoresponder
  1252. if (!dagent_oof_active(lpStoreProps)) {
  1253. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Target user has OOF inactive\n");
  1254. goto exit;
  1255. }
  1256. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Target user has OOF active\n");
  1257. // Check for presence of PR_EC_OUTOFOFFICE_MSG_W
  1258. if (lpStoreProps[1].ulPropTag == PR_EC_OUTOFOFFICE_MSG_W) {
  1259. strBody = lpStoreProps[1].Value.lpszW;
  1260. } else {
  1261. StreamPtr ptrStream;
  1262. hr = lpMDB->OpenProperty(PR_EC_OUTOFOFFICE_MSG_W, &IID_IStream, 0, 0, &~ptrStream);
  1263. if (hr == MAPI_E_NOT_FOUND) {
  1264. /* no message is ok */
  1265. } else if (hr != hrSuccess || (hr = Util::HrStreamToString(ptrStream, strBody)) != hrSuccess) {
  1266. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to download out of office message: %s", GetMAPIErrorMessage(hr));
  1267. hr = MAPI_E_FAILURE;
  1268. goto exit;
  1269. }
  1270. }
  1271. // Possibly override default subject
  1272. if (lpStoreProps[2].ulPropTag == PR_EC_OUTOFOFFICE_SUBJECT_W)
  1273. szSubject = lpStoreProps[2].Value.lpszW;
  1274. hr = lpMessage->GetProps(sptaMessageProps, 0, &cValues, &~lpMessageProps);
  1275. if (FAILED(hr)) {
  1276. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): GetProps failed(2) %x", hr);
  1277. goto exit;
  1278. }
  1279. hr = hrSuccess;
  1280. // See if we're looping
  1281. if (lpMessageProps[0].ulPropTag == PR_TRANSPORT_MESSAGE_HEADERS_A) {
  1282. if (dagent_avoid_autoreply(tokenize(lpMessageProps[0].Value.lpszA, "\n"))) {
  1283. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Avoiding OOF reply to an automated message.");
  1284. goto exit;
  1285. }
  1286. // save headers to a file so they can also be tested from the script we're runing
  1287. snprintf(szTemp, PATH_MAX, "%s/autorespond-headers.XXXXXX", TmpPath::getInstance() -> getTempPath().c_str());
  1288. fd = mkstemp(szTemp);
  1289. if (fd >= 0) {
  1290. hr = WriteOrLogError(fd, lpMessageProps[0].Value.lpszA, strlen(lpMessageProps[0].Value.lpszA));
  1291. if (hr == hrSuccess)
  1292. strTmpFile = szTemp; // pass to script
  1293. else
  1294. unlink(szTemp); // ignore headers, but still try oof script
  1295. close(fd);
  1296. fd = -1;
  1297. }
  1298. }
  1299. hr = HrGetAddress(lpAdrBook, lpMessage, PR_SENDER_ENTRYID, PR_SENDER_NAME, PR_SENDER_ADDRTYPE, PR_SENDER_EMAIL_ADDRESS, strFromName, strFromType, strFromEmail);
  1300. if (hr != hrSuccess) {
  1301. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get sender e-mail address for autoresponder, error code: 0x%08X",hr);
  1302. goto exit;
  1303. }
  1304. snprintf(szTemp, PATH_MAX, "%s/autorespond.XXXXXX", getenv("TEMP") == NULL ? "/tmp" : getenv("TEMP"));
  1305. fd = mkstemp(szTemp);
  1306. if (fd < 0) {
  1307. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to create temp file for out of office mail: %s", strerror(errno));
  1308. hr = MAPI_E_FAILURE;
  1309. goto exit;
  1310. }
  1311. // \n is on the beginning of the next header line because of snprintf and the requirement of the \n
  1312. // PATH_MAX should never be reached though.
  1313. quoted = ToQuotedBase64Header(lpRecip->wstrFullname);
  1314. snprintf(szHeader, PATH_MAX, "From: %s <%s>", quoted.c_str(), lpRecip->strSMTP.c_str());
  1315. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1316. if (hr != hrSuccess) {
  1317. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(1) %x", hr);
  1318. goto exit;
  1319. }
  1320. snprintf(szHeader, PATH_MAX, "\nTo: %ls", strFromEmail.c_str());
  1321. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1322. if (hr != hrSuccess) {
  1323. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(2) %x", hr);
  1324. goto exit;
  1325. }
  1326. // add anti-loop header for Kopano
  1327. snprintf(szHeader, PATH_MAX, "\nX-Kopano-Vacation: autorespond");
  1328. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1329. if (hr != hrSuccess) {
  1330. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(3) %x", hr);
  1331. goto exit;
  1332. }
  1333. /*
  1334. * Add anti-loop header for Exchange, see
  1335. * http://msdn.microsoft.com/en-us/library/ee219609(v=exchg.80).aspx
  1336. */
  1337. snprintf(szHeader, PATH_MAX, "\nX-Auto-Response-Suppress: All");
  1338. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1339. if (hr != hrSuccess) {
  1340. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(4) %x", hr);
  1341. goto exit;
  1342. }
  1343. /*
  1344. * Add anti-loop header for vacation(1) compatible implementations,
  1345. * see book "Sendmail" (ISBN 0596555342), section 10.9.
  1346. * RFC 3834 §3.1.8.
  1347. */
  1348. snprintf(szHeader, PATH_MAX, "\nPrecedence: bulk");
  1349. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1350. if (hr != hrSuccess) {
  1351. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(5) %x", hr);
  1352. goto exit;
  1353. }
  1354. if (lpMessageProps[3].ulPropTag == PR_SUBJECT_W)
  1355. // convert as one string because of [] characters
  1356. swprintf(szwHeader, PATH_MAX, L"%ls [%ls]", szSubject, lpMessageProps[3].Value.lpszW);
  1357. else
  1358. swprintf(szwHeader, PATH_MAX, L"%ls", szSubject);
  1359. quoted = ToQuotedBase64Header(szwHeader);
  1360. snprintf(szHeader, PATH_MAX, "\nSubject: %s", quoted.c_str());
  1361. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1362. if (hr != hrSuccess) {
  1363. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(4) %x", hr);
  1364. goto exit;
  1365. }
  1366. {
  1367. locale_t timelocale = createlocale(LC_TIME, "C");
  1368. time_t now = time(NULL);
  1369. tm local;
  1370. localtime_r(&now, &local);
  1371. strftime_l(szHeader, PATH_MAX, "\nDate: %a, %d %b %Y %T %z", &local, timelocale);
  1372. freelocale(timelocale);
  1373. }
  1374. if (WriteOrLogError(fd, szHeader, strlen(szHeader)) != hrSuccess) {
  1375. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(5) %x", hr);
  1376. goto exit;
  1377. }
  1378. snprintf(szHeader, PATH_MAX, "\nContent-Type: text/plain; charset=utf-8; format=flowed");
  1379. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1380. if (hr != hrSuccess) {
  1381. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(6) %x", hr);
  1382. goto exit;
  1383. }
  1384. snprintf(szHeader, PATH_MAX, "\nContent-Transfer-Encoding: base64");
  1385. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1386. if (hr != hrSuccess) {
  1387. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(7) %x", hr);
  1388. goto exit;
  1389. }
  1390. snprintf(szHeader, PATH_MAX, "\nMime-Version: 1.0"); // add mime-version header, so some clients show high-characters correctly
  1391. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1392. if (hr != hrSuccess) {
  1393. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(8) %x", hr);
  1394. goto exit;
  1395. }
  1396. snprintf(szHeader, PATH_MAX, "\n\n"); // last header line has double \n
  1397. hr = WriteOrLogError(fd, szHeader, strlen(szHeader));
  1398. if (hr != hrSuccess) {
  1399. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(9) %x", hr);
  1400. goto exit;
  1401. }
  1402. // write body
  1403. unquoted = convert_to<string>("UTF-8", strBody, rawsize(strBody), CHARSET_WCHAR);
  1404. quoted = base64_encode((const unsigned char*)unquoted.c_str(), unquoted.length());
  1405. hr = WriteOrLogError(fd, quoted.c_str(), quoted.length(), 76);
  1406. if (hr != hrSuccess) {
  1407. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SendOutOfOffice(): WriteOrLogError failed(10) %x", hr);
  1408. goto exit;
  1409. }
  1410. close(fd);
  1411. fd = -1;
  1412. // Args: From, To, Subject, Username, Msg_Filename
  1413. // Should run in UTF-8 to get correct strings in UTF-8 from shell_escape(wstring)
  1414. command += string(" '") + shell_escape(lpRecip->strSMTP) + string("' '") +
  1415. shell_escape(strFromEmail) + string("' '") + shell_escape(szSubject) + string("' '") + shell_escape(lpRecip->wstrUsername) + string("' '") + shell_escape(szTemp) + string("'");
  1416. // Set MESSAGE_TO_ME and MESSAGE_CC_ME in environment
  1417. strToMe = (std::string)"MESSAGE_TO_ME=" + (lpMessageProps[1].ulPropTag == PR_MESSAGE_TO_ME && lpMessageProps[1].Value.b ? "1" : "0");
  1418. strCcMe = (std::string)"MESSAGE_CC_ME=" + (lpMessageProps[2].ulPropTag == PR_MESSAGE_CC_ME && lpMessageProps[2].Value.b ? "1" : "0");
  1419. strBccMe = std::string("MESSAGE_BCC_ME=") + (lpMessageProps[4].ulPropTag == PR_EC_MESSAGE_BCC_ME && lpMessageProps[4].Value.b ? "1" : "0");
  1420. env[0] = strToMe.c_str();
  1421. env[1] = strCcMe.c_str();
  1422. strTmpFileEnv = "MAILHEADERS=" + strTmpFile;
  1423. env[2] = strTmpFileEnv.c_str();
  1424. env[3] = strBccMe.c_str();
  1425. env[4] = NULL;
  1426. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Starting autoresponder for out-of-office message");
  1427. command += " 2>&1";
  1428. if (!unix_system(strBaseCommand.c_str(), command.c_str(), env))
  1429. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Autoresponder failed");
  1430. exit:
  1431. if (fd != -1)
  1432. close(fd);
  1433. if (szTemp[0] != 0)
  1434. unlink(szTemp);
  1435. if (!strTmpFile.empty())
  1436. unlink(strTmpFile.c_str());
  1437. return hr;
  1438. }
  1439. /**
  1440. * Create an empty message for delivery
  1441. *
  1442. * @param[in] lpFolder Create the message in this folder
  1443. * @param[in] lpFallbackFolder If write access forbids the creation, fallback to this folder
  1444. * @param[out] lppDeliveryFolder The folder where the message was created
  1445. * @param[out] lppMessage The newly created message
  1446. *
  1447. * @return MAPI Error code
  1448. */
  1449. static HRESULT HrCreateMessage(IMAPIFolder *lpFolder,
  1450. IMAPIFolder *lpFallbackFolder, IMAPIFolder **lppDeliveryFolder,
  1451. IMessage **lppMessage)
  1452. {
  1453. HRESULT hr = hrSuccess;
  1454. object_ptr<IMessage> lpMessage;
  1455. hr = lpFolder->CreateMessage(nullptr, 0, &~lpMessage);
  1456. if (hr != hrSuccess && lpFallbackFolder) {
  1457. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to create new message in subfolder, using regular inbox. Error code: %08X", hr);
  1458. lpFolder = lpFallbackFolder;
  1459. hr = lpFolder->CreateMessage(nullptr, 0, &~lpMessage);
  1460. }
  1461. if (hr != hrSuccess) {
  1462. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to create new message, error code: %08X", hr);
  1463. return hr;
  1464. }
  1465. hr = lpMessage->QueryInterface(IID_IMessage, (void**)lppMessage);
  1466. if (hr != hrSuccess) {
  1467. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrCreateMessage() QueryInterface:message failed %x", hr);
  1468. return hr;
  1469. }
  1470. hr = lpFolder->QueryInterface(IID_IMAPIFolder, (void**)lppDeliveryFolder);
  1471. if (hr != hrSuccess)
  1472. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrCreateMessage() QueryInterface:folder failed %x", hr);
  1473. return hr;
  1474. }
  1475. /**
  1476. * Convert the received rfc2822 email into a MAPI message
  1477. *
  1478. * @param[in] strMail the received email
  1479. * @param[in] lpSession a MAPI Session
  1480. * @param[in] lpMsgStore The store of the delivery
  1481. * @param[in] lpAdrBook The Global Addressbook
  1482. * @param[in] lpDeliveryFolder Folder to create a new message in when the conversion fails
  1483. * @param[in] lpMessage The message to write the conversion data in
  1484. * @param[in] lpArgs delivery options
  1485. * @param[out] lppMessage The delivered message
  1486. * @param[out] lpbFallbackDelivery indicating if the message is a fallback message or not
  1487. *
  1488. * @return MAPI Error code
  1489. */
  1490. static HRESULT HrStringToMAPIMessage(const string &strMail,
  1491. IMAPISession *lpSession, IMsgStore *lpMsgStore, LPADRBOOK lpAdrBook,
  1492. IMAPIFolder *lpDeliveryFolder, IMessage *lpMessage, ECRecipient *lpRecip,
  1493. DeliveryArgs *lpArgs, IMessage **lppMessage, bool *lpbFallbackDelivery)
  1494. {
  1495. HRESULT hr = hrSuccess;
  1496. object_ptr<IMessage> lpFallbackMessage;
  1497. bool bFallback = false;
  1498. lpArgs->sDeliveryOpts.add_imap_data = lpRecip->bHasIMAP;
  1499. // Set the properties on the object
  1500. hr = IMToMAPI(lpSession, lpMsgStore, lpAdrBook, lpMessage, strMail, lpArgs->sDeliveryOpts);
  1501. if (hr != hrSuccess) {
  1502. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "E-mail parsing failed: 0x%08X. Starting fallback delivery.", hr);
  1503. // create new message
  1504. hr = lpDeliveryFolder->CreateMessage(nullptr, 0, &~lpFallbackMessage);
  1505. if (hr != hrSuccess) {
  1506. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to create fallback message, error code: 0x%08X", hr);
  1507. goto exit;
  1508. }
  1509. hr = FallbackDelivery(lpFallbackMessage, strMail);
  1510. if (hr != hrSuccess) {
  1511. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to deliver fallback message, error code: 0x%08X", hr);
  1512. goto exit;
  1513. }
  1514. // override original message with fallback version to return
  1515. lpMessage = lpFallbackMessage;
  1516. bFallback = true;
  1517. }
  1518. // return the filled (real or fallback) message
  1519. hr = lpMessage->QueryInterface(IID_IMessage, (void**)lppMessage);
  1520. if (hr != hrSuccess) {
  1521. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrStringToMAPIMessage(): QueryInterface failed %x", hr);
  1522. goto exit;
  1523. }
  1524. *lpbFallbackDelivery = bFallback;
  1525. exit:
  1526. sc->countInc("DAgent", "string_to_mapi");
  1527. // count attachments
  1528. object_ptr<IMAPITable> lppAttTable;
  1529. if (lpMessage->GetAttachmentTable(0, &~lppAttTable) == hrSuccess &&
  1530. lppAttTable != nullptr) {
  1531. ULONG countAtt = 0;
  1532. if (lppAttTable->GetRowCount(0, &countAtt) == hrSuccess &&
  1533. countAtt > 0) {
  1534. sc -> countInc("DAgent", "n_with_attachment");
  1535. sc -> countAdd("DAgent", "attachment_count", int64_t(countAtt));
  1536. }
  1537. }
  1538. // count recipients
  1539. object_ptr<IMAPITable> lppRecipTable;
  1540. if (lpMessage->GetRecipientTable(0, &~lppRecipTable) == hrSuccess &&
  1541. lppRecipTable != nullptr) {
  1542. ULONG countRecip = 0;
  1543. if (lppRecipTable->GetRowCount(0, &countRecip) == hrSuccess)
  1544. sc->countAdd("DAgent", "recipients", int64_t(countRecip));
  1545. }
  1546. return hr;
  1547. }
  1548. /**
  1549. * Check if the message was expired (delivery limit, header: Expiry-Time)
  1550. *
  1551. * @param[in] lpMessage message for delivery
  1552. * @param[out] bExpired message is expired or not
  1553. *
  1554. * @return always hrSuccess
  1555. */
  1556. static HRESULT HrMessageExpired(IMessage *lpMessage, bool *bExpired)
  1557. {
  1558. HRESULT hr = hrSuccess;
  1559. memory_ptr<SPropValue> lpsExpiryTime;
  1560. /*
  1561. * If the message has an expiry date, and it is past that time,
  1562. * skip delivering the email.
  1563. */
  1564. if (HrGetOneProp(lpMessage, PR_EXPIRY_TIME, &~lpsExpiryTime) == hrSuccess) {
  1565. time_t now = time(NULL);
  1566. time_t expire;
  1567. FileTimeToUnixTime(lpsExpiryTime->Value.ft, &expire);
  1568. if (now > expire) {
  1569. // exit with no errors
  1570. hr = hrSuccess;
  1571. *bExpired = true;
  1572. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Message was expired, not delivering");
  1573. // TODO: if a read-receipt was requested, we need to send a non-read read-receipt
  1574. goto exit;
  1575. }
  1576. }
  1577. *bExpired = false;
  1578. exit:
  1579. sc -> countInc("DAgent", *bExpired ? "msg_expired" : "msg_not_expired");
  1580. return hr;
  1581. }
  1582. /**
  1583. * Replace To recipient data in message with new recipient
  1584. *
  1585. * @param[in] lpMessage delivery message to set new recipient data in
  1586. * @param[in] lpRecip new recipient to deliver same message for
  1587. *
  1588. * @return MAPI Error code
  1589. */
  1590. static HRESULT HrOverrideRecipProps(IMessage *lpMessage, ECRecipient *lpRecip)
  1591. {
  1592. HRESULT hr = hrSuccess;
  1593. object_ptr<IMAPITable> lpRecipTable;
  1594. memory_ptr<SRestriction> lpRestrictRecipient;
  1595. SPropValue sPropRecip[4];
  1596. SPropValue sCmp[2];
  1597. bool bToMe = false;
  1598. bool bCcMe = false, bBccMe = false;
  1599. bool bRecipMe = false;
  1600. static constexpr const SizedSPropTagArray(2, sptaColumns) =
  1601. {2, {PR_RECIPIENT_TYPE, PR_ENTRYID}};
  1602. hr = lpMessage->GetRecipientTable (0, &~lpRecipTable);
  1603. if (hr != hrSuccess) {
  1604. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrOverrideRecipProps(): GetRecipientTable failed %x", hr);
  1605. return hr;
  1606. }
  1607. hr = lpRecipTable->SetColumns(sptaColumns, 0);
  1608. if (hr != hrSuccess) {
  1609. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrOverrideRecipProps(): SetColumns failed %x", hr);
  1610. return hr;
  1611. }
  1612. sCmp[0].ulPropTag = PR_ADDRTYPE_A;
  1613. sCmp[0].Value.lpszA = const_cast<char *>("ZARAFA");
  1614. sCmp[1].ulPropTag = PR_SMTP_ADDRESS_A;
  1615. sCmp[1].Value.lpszA = (char*)lpRecip->strSMTP.c_str();
  1616. hr = ECAndRestriction(
  1617. ECExistRestriction(PR_RECIPIENT_TYPE) +
  1618. ECPropertyRestriction(RELOP_EQ, PR_ADDRTYPE_A, &sCmp[0], ECRestriction::Cheap) +
  1619. ECPropertyRestriction(RELOP_EQ, PR_SMTP_ADDRESS_A, &sCmp[1], ECRestriction::Cheap)
  1620. ).CreateMAPIRestriction(&~lpRestrictRecipient, ECRestriction::Cheap);
  1621. if (hr != hrSuccess)
  1622. return hr;
  1623. hr = lpRecipTable->FindRow(lpRestrictRecipient, BOOKMARK_BEGINNING, 0);
  1624. if (hr == hrSuccess) {
  1625. rowset_ptr lpsRows;
  1626. hr = lpRecipTable->QueryRows(1, 0, &~lpsRows);
  1627. if (hr != hrSuccess) {
  1628. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrOverrideRecipProps(): QueryRows failed %x", hr);
  1629. return hr;
  1630. }
  1631. bRecipMe = (lpsRows->cRows == 1);
  1632. if (bRecipMe) {
  1633. auto lpProp = PCpropFindProp(lpsRows->aRow[0].lpProps, lpsRows->aRow[0].cValues, PR_RECIPIENT_TYPE);
  1634. if (lpProp) {
  1635. bToMe = (lpProp->Value.ul == MAPI_TO);
  1636. bCcMe = (lpProp->Value.ul == MAPI_CC);
  1637. bBccMe = lpProp->Value.ul == MAPI_BCC;
  1638. }
  1639. }
  1640. } else {
  1641. /*
  1642. * No recipients were found, message was not to me.
  1643. * Don't report error to caller, since we should set
  1644. * the properties to indicate this message is not for us.
  1645. */
  1646. hr = hrSuccess;
  1647. }
  1648. sPropRecip[0].ulPropTag = PR_MESSAGE_RECIP_ME;
  1649. sPropRecip[0].Value.b = bRecipMe;
  1650. sPropRecip[1].ulPropTag = PR_MESSAGE_TO_ME;
  1651. sPropRecip[1].Value.b = bToMe;
  1652. sPropRecip[2].ulPropTag = PR_MESSAGE_CC_ME;
  1653. sPropRecip[2].Value.b = bCcMe;
  1654. sPropRecip[3].ulPropTag = PR_EC_MESSAGE_BCC_ME;
  1655. sPropRecip[3].Value.b = bBccMe;
  1656. hr = lpMessage->SetProps(4, sPropRecip, NULL);
  1657. if (hr != hrSuccess)
  1658. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrOverrideRecipProps(): SetProps failed %x", hr);
  1659. return hr;
  1660. }
  1661. /**
  1662. * Replace To and From recipient data in fallback message with new recipient
  1663. *
  1664. * @param[in] lpMessage fallback message to set new recipient data in
  1665. * @param[in] lpRecip new recipient to deliver same message for
  1666. *
  1667. * @return MAPI Error code
  1668. */
  1669. static HRESULT HrOverrideFallbackProps(IMessage *lpMessage,
  1670. ECRecipient *lpRecip)
  1671. {
  1672. HRESULT hr = hrSuccess;
  1673. memory_ptr<ENTRYID> lpEntryIdSender;
  1674. ULONG cbEntryIdSender;
  1675. SPropValue sPropOverride[17];
  1676. ULONG ulPropPos = 0;
  1677. // Set From: and To: to the receiving party, reply will be to yourself...
  1678. // Too much information?
  1679. sPropOverride[ulPropPos].ulPropTag = PR_SENDER_NAME_W;
  1680. sPropOverride[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"System Administrator");
  1681. sPropOverride[ulPropPos].ulPropTag = PR_SENT_REPRESENTING_NAME_W;
  1682. sPropOverride[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"System Administrator");
  1683. sPropOverride[ulPropPos].ulPropTag = PR_RECEIVED_BY_NAME_W;
  1684. sPropOverride[ulPropPos++].Value.lpszW = (WCHAR *)lpRecip->wstrEmail.c_str();
  1685. // PR_SENDER_EMAIL_ADDRESS
  1686. sPropOverride[ulPropPos].ulPropTag = PR_SENDER_EMAIL_ADDRESS_A;
  1687. sPropOverride[ulPropPos++].Value.lpszA = (char *)lpRecip->strSMTP.c_str();
  1688. // PR_SENT_REPRESENTING_EMAIL_ADDRESS
  1689. sPropOverride[ulPropPos].ulPropTag = PR_SENT_REPRESENTING_EMAIL_ADDRESS_A;
  1690. sPropOverride[ulPropPos++].Value.lpszA = (char *)lpRecip->strSMTP.c_str();
  1691. // PR_RECEIVED_BY_EMAIL_ADDRESS
  1692. sPropOverride[ulPropPos].ulPropTag = PR_RECEIVED_BY_EMAIL_ADDRESS_A;
  1693. sPropOverride[ulPropPos++].Value.lpszA = (char *)lpRecip->strSMTP.c_str();
  1694. sPropOverride[ulPropPos].ulPropTag = PR_RCVD_REPRESENTING_EMAIL_ADDRESS_A;
  1695. sPropOverride[ulPropPos++].Value.lpszA = (char *)lpRecip->strSMTP.c_str();
  1696. // PR_SENDER_ADDRTYPE
  1697. sPropOverride[ulPropPos].ulPropTag = PR_SENDER_ADDRTYPE_W;
  1698. sPropOverride[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"SMTP");
  1699. // PR_SENT_REPRESENTING_ADDRTYPE
  1700. sPropOverride[ulPropPos].ulPropTag = PR_SENT_REPRESENTING_ADDRTYPE_W;
  1701. sPropOverride[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"SMTP");
  1702. // PR_RECEIVED_BY_ADDRTYPE
  1703. sPropOverride[ulPropPos].ulPropTag = PR_RECEIVED_BY_ADDRTYPE_W;
  1704. sPropOverride[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"SMTP");
  1705. sPropOverride[ulPropPos].ulPropTag = PR_RCVD_REPRESENTING_ADDRTYPE_W;
  1706. sPropOverride[ulPropPos++].Value.lpszW = const_cast<wchar_t *>(L"SMTP");
  1707. // PR_SENDER_SEARCH_KEY
  1708. sPropOverride[ulPropPos].ulPropTag = PR_SENDER_SEARCH_KEY;
  1709. sPropOverride[ulPropPos].Value.bin.cb = lpRecip->sSearchKey.cb;
  1710. sPropOverride[ulPropPos++].Value.bin.lpb = lpRecip->sSearchKey.lpb;
  1711. // PR_RECEIVED_BY_SEARCH_KEY (set as previous)
  1712. sPropOverride[ulPropPos].ulPropTag = PR_RECEIVED_BY_SEARCH_KEY;
  1713. sPropOverride[ulPropPos].Value.bin.cb = lpRecip->sSearchKey.cb;
  1714. sPropOverride[ulPropPos++].Value.bin.lpb = lpRecip->sSearchKey.lpb;
  1715. // PR_SENT_REPRESENTING_SEARCH_KEY (set as previous)
  1716. sPropOverride[ulPropPos].ulPropTag = PR_SENT_REPRESENTING_SEARCH_KEY;
  1717. sPropOverride[ulPropPos].Value.bin.cb = lpRecip->sSearchKey.cb;
  1718. sPropOverride[ulPropPos++].Value.bin.lpb = lpRecip->sSearchKey.lpb;
  1719. hr = ECCreateOneOff((LPTSTR)lpRecip->wstrFullname.c_str(), (LPTSTR)L"SMTP", (LPTSTR)convert_to<wstring>(lpRecip->strSMTP).c_str(),
  1720. MAPI_UNICODE | MAPI_SEND_NO_RICH_INFO, &cbEntryIdSender, &~lpEntryIdSender);
  1721. if (hr == hrSuccess) {
  1722. // PR_SENDER_ENTRYID
  1723. sPropOverride[ulPropPos].ulPropTag = PR_SENDER_ENTRYID;
  1724. sPropOverride[ulPropPos].Value.bin.cb = cbEntryIdSender;
  1725. sPropOverride[ulPropPos++].Value.bin.lpb = reinterpret_cast<BYTE *>(lpEntryIdSender.get());
  1726. // PR_RECEIVED_BY_ENTRYID
  1727. sPropOverride[ulPropPos].ulPropTag = PR_RECEIVED_BY_ENTRYID;
  1728. sPropOverride[ulPropPos].Value.bin.cb = cbEntryIdSender;
  1729. sPropOverride[ulPropPos++].Value.bin.lpb = reinterpret_cast<BYTE *>(lpEntryIdSender.get());
  1730. // PR_SENT_REPRESENTING_ENTRYID
  1731. sPropOverride[ulPropPos].ulPropTag = PR_SENT_REPRESENTING_ENTRYID;
  1732. sPropOverride[ulPropPos].Value.bin.cb = cbEntryIdSender;
  1733. sPropOverride[ulPropPos++].Value.bin.lpb = reinterpret_cast<BYTE *>(lpEntryIdSender.get());
  1734. } else {
  1735. hr = hrSuccess;
  1736. }
  1737. hr = lpMessage->SetProps(ulPropPos, sPropOverride, NULL);
  1738. if (hr != hrSuccess)
  1739. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to set fallback delivery properties: 0x%08X", hr);
  1740. return hr;
  1741. }
  1742. /**
  1743. * Set new To recipient data in message
  1744. *
  1745. * @param[in] lpMessage message to update recipient data in
  1746. * @param[in] lpRecip recipient data to use
  1747. *
  1748. * @return MAPI error code
  1749. */
  1750. static HRESULT HrOverrideReceivedByProps(IMessage *lpMessage,
  1751. ECRecipient *lpRecip)
  1752. {
  1753. SPropValue sPropReceived[5];
  1754. /* First set the PR_RECEIVED_BY_* properties */
  1755. sPropReceived[0].ulPropTag = PR_RECEIVED_BY_ADDRTYPE_A;
  1756. sPropReceived[0].Value.lpszA = (char *)lpRecip->strAddrType.c_str();
  1757. sPropReceived[1].ulPropTag = PR_RECEIVED_BY_EMAIL_ADDRESS_W;
  1758. sPropReceived[1].Value.lpszW = (WCHAR *)lpRecip->wstrUsername.c_str();
  1759. sPropReceived[2].ulPropTag = PR_RECEIVED_BY_ENTRYID;
  1760. sPropReceived[2].Value.bin.cb = lpRecip->sEntryId.cb;
  1761. sPropReceived[2].Value.bin.lpb = lpRecip->sEntryId.lpb;
  1762. sPropReceived[3].ulPropTag = PR_RECEIVED_BY_NAME_W;
  1763. sPropReceived[3].Value.lpszW = (WCHAR *)lpRecip->wstrFullname.c_str();
  1764. sPropReceived[4].ulPropTag = PR_RECEIVED_BY_SEARCH_KEY;
  1765. sPropReceived[4].Value.bin.cb = lpRecip->sSearchKey.cb;
  1766. sPropReceived[4].Value.bin.lpb = lpRecip->sSearchKey.lpb;
  1767. HRESULT hr = lpMessage->SetProps(5, sPropReceived, NULL);
  1768. if (hr != hrSuccess) {
  1769. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to set RECEIVED_BY properties: 0x%08X", hr);
  1770. return hr;
  1771. }
  1772. return hrSuccess;
  1773. }
  1774. /**
  1775. * Copy a delivered message to another recipient
  1776. *
  1777. * @param[in] lpOrigMessage The original delivered message
  1778. * @param[in] lpDeliverFolder The delivery folder of the new message
  1779. * @param[in] lpRecip recipient data to use
  1780. * @param[in] lpFallbackFolder Fallback folder incase lpDeliverFolder cannot be delivered to
  1781. * @param[in] bFallbackDelivery lpOrigMessage is a fallback delivery message
  1782. * @param[out] lppFolder folder the new message was created in
  1783. * @param[out] lppMessage the newly copied message
  1784. *
  1785. * @return MAPI Error code
  1786. */
  1787. static HRESULT HrCopyMessageForDelivery(IMessage *lpOrigMessage,
  1788. IMAPIFolder *lpDeliverFolder, ECRecipient *lpRecip,
  1789. IMAPIFolder *lpFallbackFolder, bool bFallbackDelivery,
  1790. IMAPIFolder **lppFolder = NULL, IMessage **lppMessage = NULL)
  1791. {
  1792. HRESULT hr = hrSuccess;
  1793. object_ptr<IMessage> lpMessage;
  1794. object_ptr<IMAPIFolder> lpFolder;
  1795. helpers::MAPIPropHelperPtr ptrArchiveHelper;
  1796. static constexpr const SizedSPropTagArray(13, sptaReceivedBy) = {
  1797. 13, {
  1798. /* Overriden by HrOverrideRecipProps() */
  1799. PR_MESSAGE_RECIP_ME,
  1800. PR_MESSAGE_TO_ME,
  1801. PR_MESSAGE_CC_ME,
  1802. /* HrOverrideReceivedByProps() */
  1803. PR_RECEIVED_BY_ADDRTYPE,
  1804. PR_RECEIVED_BY_EMAIL_ADDRESS,
  1805. PR_RECEIVED_BY_ENTRYID,
  1806. PR_RECEIVED_BY_NAME,
  1807. PR_RECEIVED_BY_SEARCH_KEY,
  1808. /* Written by rules */
  1809. PR_LAST_VERB_EXECUTED,
  1810. PR_LAST_VERB_EXECUTION_TIME,
  1811. PR_ICON_INDEX,
  1812. }
  1813. };
  1814. static constexpr const SizedSPropTagArray(12, sptaFallback) = {
  1815. 12, {
  1816. /* Overriden by HrOverrideFallbackProps() */
  1817. PR_SENDER_ADDRTYPE,
  1818. PR_SENDER_EMAIL_ADDRESS,
  1819. PR_SENDER_ENTRYID,
  1820. PR_SENDER_NAME,
  1821. PR_SENDER_SEARCH_KEY,
  1822. PR_SENT_REPRESENTING_ADDRTYPE,
  1823. PR_SENT_REPRESENTING_EMAIL_ADDRESS,
  1824. PR_SENT_REPRESENTING_ENTRYID,
  1825. PR_SENT_REPRESENTING_NAME,
  1826. PR_SENT_REPRESENTING_SEARCH_KEY,
  1827. PR_RCVD_REPRESENTING_ADDRTYPE,
  1828. PR_RCVD_REPRESENTING_EMAIL_ADDRESS,
  1829. }
  1830. };
  1831. hr = HrCreateMessage(lpDeliverFolder, lpFallbackFolder, &~lpFolder, &~lpMessage);
  1832. if (hr != hrSuccess) {
  1833. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrCopyMessageForDelivery(): HrCreateMessage failed %x", hr);
  1834. return hr;
  1835. }
  1836. /* Copy message, exclude all previously set properties (Those are recipient dependent) */
  1837. hr = lpOrigMessage->CopyTo(0, NULL, sptaReceivedBy, 0, NULL,
  1838. &IID_IMessage, lpMessage, 0, NULL);
  1839. if (hr != hrSuccess) {
  1840. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrCopyMessageForDelivery(): CopyTo failed %x", hr);
  1841. return hr;
  1842. }
  1843. // For a fallback, remove some more properties
  1844. if (bFallbackDelivery)
  1845. lpMessage->DeleteProps(sptaFallback, 0);
  1846. // Make sure the message is not attached to an archive
  1847. hr = helpers::MAPIPropHelper::Create(MAPIPropPtr(lpMessage, true), &ptrArchiveHelper);
  1848. if (hr != hrSuccess) {
  1849. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrCopyMessageForDelivery(): helpers::MAPIPropHelper::Create failed %x", hr);
  1850. return hr;
  1851. }
  1852. hr = ptrArchiveHelper->DetachFromArchives();
  1853. if (hr != hrSuccess) {
  1854. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrCopyMessageForDelivery(): DetachFromArchives failed %x", hr);
  1855. return hr;
  1856. }
  1857. if (lpRecip->bHasIMAP)
  1858. hr = Util::HrCopyIMAPData(lpOrigMessage, lpMessage);
  1859. else
  1860. hr = Util::HrDeleteIMAPData(lpMessage); // make sure the imap data is not set for this user.
  1861. if (hr != hrSuccess) {
  1862. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HrCopyMessageForDelivery(): IMAP handling failed %x", hr);
  1863. return hr;
  1864. }
  1865. if (lppFolder)
  1866. lpFolder->QueryInterface(IID_IMAPIFolder, (void**)lppFolder);
  1867. if (lppMessage)
  1868. lpMessage->QueryInterface(IID_IMessage, (void**)lppMessage);
  1869. return hrSuccess;
  1870. }
  1871. /**
  1872. * Make a new MAPI session under a specific username
  1873. *
  1874. * @param[in] lpArgs delivery options
  1875. * @param[in] szUsername username to create mapi session for
  1876. * @param[out] lppSession new MAPI session for user
  1877. * @param[in] bSuppress suppress logging (default: false)
  1878. *
  1879. * @return MAPI Error code
  1880. */
  1881. static HRESULT HrGetSession(const DeliveryArgs *lpArgs,
  1882. const WCHAR *szUsername, IMAPISession **lppSession, bool bSuppress = false)
  1883. {
  1884. HRESULT hr = hrSuccess;
  1885. struct passwd *pwd = NULL;
  1886. string strUnixUser;
  1887. hr = HrOpenECSession(lppSession, "spooler/dagent", PROJECT_SVN_REV_STR,
  1888. szUsername, L"", lpArgs->strPath.c_str(), 0,
  1889. g_lpConfig->GetSetting("sslkey_file", "", NULL),
  1890. g_lpConfig->GetSetting("sslkey_pass", "", NULL));
  1891. if (hr == hrSuccess)
  1892. return hrSuccess;
  1893. // if connecting fails, the mailer should try to deliver again.
  1894. switch (hr) {
  1895. case MAPI_E_NETWORK_ERROR:
  1896. if (!bSuppress)
  1897. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to connect to storage server for user %ls, using socket: '%s'", szUsername, lpArgs->strPath.c_str());
  1898. break;
  1899. // MAPI_E_NO_ACCESS or MAPI_E_LOGON_FAILED are fatal (user does not exist)
  1900. case MAPI_E_LOGON_FAILED:
  1901. // running dagent as Unix user != lpRecip->strUsername and ! listed in local_admin_user, which gives this error too
  1902. if (!bSuppress)
  1903. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Access denied or connection failed for user %ls, using socket: '%s', error code: 0x%08X", szUsername, lpArgs->strPath.c_str(), hr);
  1904. // so also log userid we're running as
  1905. pwd = getpwuid(getuid());
  1906. strUnixUser = (pwd != NULL && pwd->pw_name != NULL) ? pwd->pw_name : stringify(getuid());
  1907. if (!bSuppress)
  1908. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Current uid:%d username:%s", getuid(), strUnixUser.c_str());
  1909. break;
  1910. default:
  1911. if (!bSuppress)
  1912. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to login for user %ls, error code: 0x%08X", szUsername, hr);
  1913. break;
  1914. }
  1915. return hr;
  1916. }
  1917. /**
  1918. * Run rules on a message and/or send an oof email before writing it
  1919. * to the server.
  1920. *
  1921. * @param[in] lpAdrBook Addressbook to use during rules
  1922. * @param[in] lpStore Store the message will be written too
  1923. * @param[in] lpInbox Inbox of the user message is being delivered to
  1924. * @param[in] lpFolder Actual delivery folder of message
  1925. * @param[in,out] lppMessage message being delivered, can return another message due to rules
  1926. * @param[in] lpRecip recipient that is delivered to
  1927. * @param[in] lpArgs delivery options
  1928. *
  1929. * @return MAPI Error code
  1930. */
  1931. static HRESULT HrPostDeliveryProcessing(pym_plugin_intf *lppyMapiPlugin,
  1932. LPADRBOOK lpAdrBook, LPMDB lpStore, IMAPIFolder *lpInbox,
  1933. IMAPIFolder *lpFolder, IMessage **lppMessage, ECRecipient *lpRecip,
  1934. DeliveryArgs *lpArgs)
  1935. {
  1936. HRESULT hr = hrSuccess;
  1937. object_ptr<IMAPISession> lpUserSession;
  1938. SPropValuePtr ptrProp;
  1939. hr = HrOpenECSession(&~lpUserSession, "spooler/dagent:delivery",
  1940. PROJECT_SVN_REV_STR, lpRecip->wstrUsername.c_str(), L"",
  1941. lpArgs->strPath.c_str(), EC_PROFILE_FLAGS_NO_NOTIFICATIONS,
  1942. g_lpConfig->GetSetting("sslkey_file", "", NULL),
  1943. g_lpConfig->GetSetting("sslkey_pass", "", NULL));
  1944. if (hr != hrSuccess)
  1945. return hr;
  1946. if(FNeedsAutoAccept(lpStore, *lppMessage)) {
  1947. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Starting MR autoaccepter");
  1948. hr = HrAutoAccept(lpRecip, lpStore, *lppMessage);
  1949. if(hr == hrSuccess) {
  1950. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Autoaccept processing completed successfully. Skipping further processing.");
  1951. // The MR autoaccepter has processed the message. Skip any further work on this message: dont
  1952. // run rules and dont send new mail notifications (The message should be deleted now)
  1953. return MAPI_E_CANCEL;
  1954. }
  1955. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Autoaccept processing failed, proceeding with rules processing: %s (%x).",
  1956. GetMAPIErrorMessage(hr), hr);
  1957. // The MR autoaccepter did not run properly. This could be correct behaviour; for example the
  1958. // autoaccepter may want to defer accepting to a human controller. This means we have to continue
  1959. // processing as if the autoaccepter was not used
  1960. hr = hrSuccess;
  1961. }
  1962. else if (FNeedsAutoProcessing(*lppMessage)) {
  1963. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Starting MR auto processing");
  1964. hr = HrAutoProcess(lpRecip, lpStore, *lppMessage);
  1965. if (hr == hrSuccess)
  1966. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Automatic MR processing successful.");
  1967. else
  1968. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Automatic MR processing failed: %s (%x).",
  1969. GetMAPIErrorMessage(hr), hr);
  1970. }
  1971. if (lpFolder == lpInbox) {
  1972. // process rules for the inbox
  1973. hr = HrProcessRules(convert_to<std::string>(lpRecip->wstrUsername), lppyMapiPlugin, lpUserSession, lpAdrBook, lpStore, lpInbox, lppMessage, sc);
  1974. if (hr == MAPI_E_CANCEL)
  1975. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "Message canceled by rule");
  1976. else if (hr != hrSuccess)
  1977. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to process rules, error code: 0x%08X",hr);
  1978. // continue, still send possible out-of-office message
  1979. }
  1980. // do not send vacation message for junk messages
  1981. if (lpArgs->ulDeliveryMode != DM_JUNK &&
  1982. // do not send vacation message on delegated messages
  1983. (HrGetOneProp(*lppMessage, PR_DELEGATED_BY_RULE, &~ptrProp) != hrSuccess || ptrProp->Value.b == FALSE))
  1984. SendOutOfOffice(lpAdrBook, lpStore, *lppMessage, lpRecip, lpArgs->strAutorespond);
  1985. return hr;
  1986. }
  1987. /**
  1988. * Find spam header if needed, and mark delivery as spam delivery if
  1989. * header found.
  1990. *
  1991. * @param[in] strMail rfc2822 email being delivered
  1992. * @param[in,out] lpArgs delivery options
  1993. *
  1994. * @return MAPI Error code
  1995. */
  1996. static HRESULT FindSpamMarker(const std::string &strMail,
  1997. DeliveryArgs *lpArgs)
  1998. {
  1999. HRESULT hr = hrSuccess;
  2000. const char *szHeader = g_lpConfig->GetSetting("spam_header_name", "", NULL);
  2001. const char *szValue = g_lpConfig->GetSetting("spam_header_value", "", NULL);
  2002. size_t end, pos;
  2003. string match;
  2004. string strHeaders;
  2005. if (!szHeader || !szValue)
  2006. goto exit;
  2007. // find end of headers
  2008. end = strMail.find("\r\n\r\n");
  2009. if (end == string::npos)
  2010. goto exit;
  2011. end += 2;
  2012. // copy headers in upper case, need to resize destination first
  2013. strHeaders.resize(end);
  2014. transform(strMail.begin(), strMail.begin() +end, strHeaders.begin(), ::toupper);
  2015. match = strToUpper(std::string("\r\n") + szHeader);
  2016. // find header
  2017. pos = strHeaders.find(match.c_str());
  2018. if (pos == string::npos)
  2019. goto exit;
  2020. // skip header and find end of line
  2021. pos += match.length();
  2022. end = strHeaders.find("\r\n", pos);
  2023. match = strToUpper(szValue);
  2024. // find value in header line (no header continuations supported here)
  2025. pos = strHeaders.find(match.c_str(), pos);
  2026. if (pos == string::npos || pos > end)
  2027. goto exit;
  2028. // found, override delivery to junkmail folder
  2029. lpArgs->ulDeliveryMode = DM_JUNK;
  2030. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Spam marker found in e-mail, delivering to junk-mail folder");
  2031. exit:
  2032. sc -> countInc("DAgent", lpArgs->ulDeliveryMode == DM_JUNK ? "is_spam" : "is_ham");
  2033. return hr;
  2034. }
  2035. /**
  2036. * Deliver an email (source is either rfc2822 or previous delivered
  2037. * mapi message) to a specific recipient.
  2038. *
  2039. * @param[in] lpSession MAPI session (user session when not in LMTP mode, else admin session)
  2040. * @param[in] lpStore default store for lpSession (user store when not in LMTP mode, else admin store)
  2041. * @param[in] bIsAdmin indicates that lpSession and lpStore are an admin session and store (true in LMTP mode)
  2042. * @param[in] lpAdrBook Global Addressbook
  2043. * @param[in] lpOrigMessage a previously delivered message, if any
  2044. * @param[in] bFallbackDelivery previously delivered message was a fallback message
  2045. * @param[in] strMail original received rfc2822 email
  2046. * @param[in] lpRecip recipient to deliver message to
  2047. * @param[in] lpArgs delivery options
  2048. * @param[out] lppMessage the newly delivered message
  2049. * @param[out] lpbFallbackDelivery newly delivered message is a fallback message
  2050. *
  2051. * @return MAPI Error code
  2052. */
  2053. static HRESULT ProcessDeliveryToRecipient(pym_plugin_intf *lppyMapiPlugin,
  2054. IMAPISession *lpSession, IMsgStore *lpStore, bool bIsAdmin,
  2055. LPADRBOOK lpAdrBook, IMessage *lpOrigMessage, bool bFallbackDelivery,
  2056. const std::string &strMail, ECRecipient *lpRecip, DeliveryArgs *lpArgs,
  2057. IMessage **lppMessage, bool *lpbFallbackDelivery)
  2058. {
  2059. HRESULT hr = hrSuccess;
  2060. object_ptr<IMsgStore> lpTargetStore;
  2061. object_ptr<IMAPIFolder> lpTargetFolder, lpFolder, lpInbox;
  2062. object_ptr<IMessage> lpDeliveryMessage, lpMessageTmp;
  2063. object_ptr<IABContainer> lpAddrDir;
  2064. ULONG ulResult = 0;
  2065. ULONG ulNewMailNotify = 0;
  2066. // single user deliver did not lookup the user
  2067. if (lpRecip->strSMTP.empty()) {
  2068. hr = OpenResolveAddrFolder(lpAdrBook, &~lpAddrDir);
  2069. if (hr != hrSuccess) {
  2070. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): OpenResolveAddrFolder failed %x", hr);
  2071. return hr;
  2072. }
  2073. hr = ResolveUser(lpAddrDir, lpRecip);
  2074. if (hr != hrSuccess) {
  2075. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): ResolveUser failed %x", hr);
  2076. return hr;
  2077. }
  2078. }
  2079. hr = HrGetDeliveryStoreAndFolder(lpSession, lpStore, lpRecip, lpArgs, &~lpTargetStore, &~lpInbox, &~lpTargetFolder);
  2080. if (hr != hrSuccess) {
  2081. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): HrGetDeliveryStoreAndFolder failed %x", hr);
  2082. return hr;
  2083. }
  2084. if (!lpOrigMessage) {
  2085. /* No message was provided, we have to construct it personally */
  2086. bool bExpired = false;
  2087. hr = HrCreateMessage(lpTargetFolder, lpInbox, &~lpFolder, &~lpMessageTmp);
  2088. if (hr != hrSuccess) {
  2089. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): HrCreateMessage failed %x", hr);
  2090. return hr;
  2091. }
  2092. hr = HrStringToMAPIMessage(strMail, lpSession, lpTargetStore, lpAdrBook, lpFolder, lpMessageTmp, lpRecip, lpArgs, &~lpDeliveryMessage, &bFallbackDelivery);
  2093. if (hr != hrSuccess) {
  2094. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): HrStringToMAPIMessage failed %x", hr);
  2095. return hr;
  2096. }
  2097. /*
  2098. * Check if the message has expired.
  2099. */
  2100. hr = HrMessageExpired(lpDeliveryMessage, &bExpired);
  2101. if (hr != hrSuccess) {
  2102. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): HrMessageExpired failed %x", hr);
  2103. return hr;
  2104. }
  2105. if (bExpired)
  2106. /* Set special error code for callers */
  2107. return MAPI_W_CANCEL_MESSAGE;
  2108. hr = lppyMapiPlugin->MessageProcessing("PostConverting", lpSession, lpAdrBook, NULL, NULL, lpDeliveryMessage, &ulResult);
  2109. if (hr != hrSuccess) {
  2110. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): MessageProcessing failed %x", hr);
  2111. return hr;
  2112. }
  2113. // TODO do something with ulResult
  2114. } else {
  2115. /* Copy message to prepare for new delivery */
  2116. hr = HrCopyMessageForDelivery(lpOrigMessage, lpTargetFolder, lpRecip, lpInbox, bFallbackDelivery, &~lpFolder, &~lpDeliveryMessage);
  2117. if (hr != hrSuccess) {
  2118. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): HrCopyMessageForDelivery failed %x", hr);
  2119. return hr;
  2120. }
  2121. }
  2122. hr = HrOverrideRecipProps(lpDeliveryMessage, lpRecip);
  2123. if (hr != hrSuccess) {
  2124. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): HrOverrideRecipProps failed %x", hr);
  2125. return hr;
  2126. }
  2127. if (bFallbackDelivery) {
  2128. hr = HrOverrideFallbackProps(lpDeliveryMessage, lpRecip);
  2129. if (hr != hrSuccess) {
  2130. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): HrOverrideFallbackProps failed %x", hr);
  2131. return hr;
  2132. }
  2133. } else {
  2134. hr = HrOverrideReceivedByProps(lpDeliveryMessage, lpRecip);
  2135. if (hr != hrSuccess) {
  2136. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): HrOverrideReceivedByProps failed %x", hr);
  2137. return hr;
  2138. }
  2139. }
  2140. hr = lppyMapiPlugin->MessageProcessing("PreDelivery", lpSession, lpAdrBook, lpTargetStore, lpTargetFolder, lpDeliveryMessage, &ulResult);
  2141. if (hr != hrSuccess) {
  2142. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): MessageProcessing(2) failed %x", hr);
  2143. return hr;
  2144. }
  2145. // TODO do something with ulResult
  2146. if (ulResult == MP_STOP_SUCCESS) {
  2147. if (lppMessage)
  2148. lpDeliveryMessage->QueryInterface(IID_IMessage, (void**)lppMessage);
  2149. if (lpbFallbackDelivery)
  2150. *lpbFallbackDelivery = bFallbackDelivery;
  2151. return hr;
  2152. }
  2153. // Do rules & out-of-office
  2154. hr = HrPostDeliveryProcessing(lppyMapiPlugin, lpAdrBook, lpTargetStore, lpInbox, lpTargetFolder, &+lpDeliveryMessage, lpRecip, lpArgs);
  2155. if (hr != MAPI_E_CANCEL) {
  2156. // ignore other errors for rules, still want to save the delivered message
  2157. // Save message changes, message becomes visible for the user
  2158. hr = lpDeliveryMessage->SaveChanges(KEEP_OPEN_READWRITE);
  2159. if (hr != hrSuccess) {
  2160. if (hr == MAPI_E_STORE_FULL)
  2161. // make sure the error is printed on stderr, so this will be bounced as error by the MTA.
  2162. // use cerr to avoid quiet mode.
  2163. fprintf(stderr, "Store of user %ls is over quota limit.\n", lpRecip->wstrUsername.c_str());
  2164. else
  2165. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to commit message: 0x%08X", hr);
  2166. return hr;
  2167. }
  2168. hr = lppyMapiPlugin->MessageProcessing("PostDelivery", lpSession, lpAdrBook, lpTargetStore, lpTargetFolder, lpDeliveryMessage, &ulResult);
  2169. if (hr != hrSuccess) {
  2170. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToRecipient(): MessageProcessing(3) failed %x", hr);
  2171. return hr;
  2172. }
  2173. // TODO do something with ulResult
  2174. if (parseBool(g_lpConfig->GetSetting("archive_on_delivery"))) {
  2175. MAPISessionPtr ptrAdminSession;
  2176. ArchivePtr ptrArchive;
  2177. if (bIsAdmin)
  2178. hr = lpSession->QueryInterface(ptrAdminSession.iid(), &~ptrAdminSession);
  2179. else {
  2180. const char *server = g_lpConfig->GetSetting("server_socket");
  2181. server = GetServerUnixSocket(server); // let environment override if present
  2182. hr = HrOpenECAdminSession(&~ptrAdminSession, "spooler/dagent:system", PROJECT_SVN_REV_STR, server, EC_PROFILE_FLAGS_NO_NOTIFICATIONS, g_lpConfig->GetSetting("sslkey_file", "", NULL), g_lpConfig->GetSetting("sslkey_pass", "", NULL));
  2183. }
  2184. if (hr != hrSuccess) {
  2185. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open admin session for archive access: 0x%08X", hr);
  2186. return hr;
  2187. }
  2188. hr = Archive::Create(ptrAdminSession, &ptrArchive);
  2189. if (hr != hrSuccess) {
  2190. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to instantiate archive object: 0x%08X", hr);
  2191. return hr;
  2192. }
  2193. hr = ptrArchive->HrArchiveMessageForDelivery(lpDeliveryMessage);
  2194. if (hr != hrSuccess) {
  2195. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to archive message: 0x%08X", hr);
  2196. Util::HrDeleteMessage(lpSession, lpDeliveryMessage);
  2197. return hr;
  2198. }
  2199. }
  2200. if (lpArgs->bNewmailNotify) {
  2201. ulNewMailNotify = TRUE;
  2202. hr = lppyMapiPlugin->RequestCallExecution("SendNewMailNotify", lpSession, lpAdrBook, lpTargetStore, lpTargetFolder, lpDeliveryMessage, &ulNewMailNotify, &ulResult);
  2203. if (hr != hrSuccess) {
  2204. // Plugin failed so fallback on the original state
  2205. ulNewMailNotify = lpArgs->bNewmailNotify;
  2206. hr = hrSuccess;
  2207. }
  2208. if (ulNewMailNotify == true) {
  2209. hr = HrNewMailNotification(lpTargetStore, lpDeliveryMessage);
  2210. if (hr != hrSuccess)
  2211. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to send 'New Mail' notification, error code: 0x%08X", hr);
  2212. else
  2213. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Send 'New Mail' notification");
  2214. hr = hrSuccess;
  2215. }
  2216. }
  2217. }
  2218. if (lppMessage)
  2219. lpDeliveryMessage->QueryInterface(IID_IMessage, (void**)lppMessage);
  2220. if (lpbFallbackDelivery)
  2221. *lpbFallbackDelivery = bFallbackDelivery;
  2222. return hr;
  2223. }
  2224. /**
  2225. * Log that the message was expired, and send that response for every given LMTP received recipient
  2226. *
  2227. * @param[in] start Start of recipient list
  2228. * @param[in] end End of recipient list
  2229. */
  2230. static void RespondMessageExpired(recipients_t::const_iterator iter,
  2231. recipients_t::const_iterator end)
  2232. {
  2233. convert_context converter;
  2234. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Message was expired, not delivering");
  2235. for (; iter != end; ++iter)
  2236. (*iter)->wstrDeliveryStatus = L"250 2.4.7 %ls Delivery time expired";
  2237. }
  2238. /**
  2239. * For a specific storage server, deliver the same message to a list of
  2240. * recipients. This makes sure this message is correctly single
  2241. * instanced on this server.
  2242. *
  2243. * In this function, it is mandatory to have processed all recipients
  2244. * in the list.
  2245. *
  2246. * @param[in] lpUserSession optional session of one user the message is being delivered to (cmdline dagent, NULL on LMTP mode)
  2247. * @param[in] lpMessage an already delivered message
  2248. * @param[in] bFallbackDelivery already delivered message is a fallback message
  2249. * @param[in] strMail the rfc2822 received email
  2250. * @param[in] strServer uri of the storage server to connect to
  2251. * @param[in] listRecipients list of recipients present on the server connecting to
  2252. * @param[in] lpAdrBook Global addressbook
  2253. * @param[in] lpArgs delivery options
  2254. * @param[out] lppMessage The newly delivered message
  2255. * @param[out] lpbFallbackDelivery newly delivered message is a fallback message
  2256. *
  2257. * @return MAPI Error code
  2258. */
  2259. static HRESULT ProcessDeliveryToServer(pym_plugin_intf *lppyMapiPlugin,
  2260. IMAPISession *lpUserSession, IMessage *lpMessage, bool bFallbackDelivery,
  2261. const std::string &strMail, const std::string &strServer,
  2262. const recipients_t &listRecipients, LPADRBOOK lpAdrBook,
  2263. DeliveryArgs *lpArgs, IMessage **lppMessage, bool *lpbFallbackDelivery)
  2264. {
  2265. HRESULT hr = hrSuccess;
  2266. object_ptr<IMAPISession> lpSession;
  2267. object_ptr<IMsgStore> lpStore;
  2268. object_ptr<IMessage> lpOrigMessage;
  2269. bool bFallbackDeliveryTmp = false;
  2270. convert_context converter;
  2271. sc -> countInc("DAgent", "to_server");
  2272. // if we already had a message, we can create a copy.
  2273. if (lpMessage)
  2274. lpMessage->QueryInterface(IID_IMessage, &~lpOrigMessage);
  2275. if (lpUserSession)
  2276. hr = lpUserSession->QueryInterface(IID_IMAPISession, &~lpSession);
  2277. else
  2278. hr = HrOpenECAdminSession(&~lpSession, "spooler/dagent/delivery:system",
  2279. PROJECT_SVN_REV_STR, strServer.c_str(),
  2280. EC_PROFILE_FLAGS_NO_NOTIFICATIONS,
  2281. g_lpConfig->GetSetting("sslkey_file", "", NULL),
  2282. g_lpConfig->GetSetting("sslkey_pass", "", NULL));
  2283. if (hr != hrSuccess || (hr = HrOpenDefaultStore(lpSession, &~lpStore)) != hrSuccess) {
  2284. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open default store for system account, error code: 0x%08X", hr);
  2285. // notify LMTP client soft error to try again later
  2286. for (const auto &recip : listRecipients)
  2287. // error will be shown in postqueue status in postfix, probably too in other serves and mail syslog service
  2288. recip->wstrDeliveryStatus = L"450 4.5.0 %ls network or permissions error to storage server: " + wstringify(hr, true);
  2289. return hr;
  2290. }
  2291. for (auto iter = listRecipients.cbegin(); iter != listRecipients.end(); ++iter) {
  2292. const auto &recip = *iter;
  2293. object_ptr<IMessage> lpMessageTmp;
  2294. /*
  2295. * Normal error codes must be ignored, since we want to attempt to deliver the email to all users,
  2296. * however when the error code MAPI_W_CANCEL_MESSAGE was provided, the message has expired and it is
  2297. * pointles to continue delivering the mail. However we must continue looping through all recipients
  2298. * to inform the MTA we did handle the email properly.
  2299. */
  2300. hr = ProcessDeliveryToRecipient(lppyMapiPlugin, lpSession,
  2301. lpStore, lpUserSession == NULL, lpAdrBook, lpOrigMessage,
  2302. bFallbackDelivery, strMail, recip, lpArgs, &~lpMessageTmp,
  2303. &bFallbackDeliveryTmp);
  2304. if (hr == hrSuccess || hr == MAPI_E_CANCEL) {
  2305. if (hr == hrSuccess) {
  2306. memory_ptr<SPropValue> lpMessageId, lpSubject;
  2307. wstring wMessageId;
  2308. if (HrGetOneProp(lpMessageTmp, PR_INTERNET_MESSAGE_ID_W, &~lpMessageId) == hrSuccess)
  2309. wMessageId = lpMessageId->Value.lpszW;
  2310. HrGetOneProp(lpMessageTmp, PR_SUBJECT_W, &~lpSubject);
  2311. g_lpLogger->Log(EC_LOGLEVEL_INFO,
  2312. "Delivered message to '%ls', Subject: \"%ls\", Message-Id: %ls, size %lu",
  2313. recip->wstrUsername.c_str(),
  2314. (lpSubject != NULL) ? lpSubject->Value.lpszW : L"<none>",
  2315. wMessageId.c_str(), static_cast<unsigned long>(strMail.size()));
  2316. }
  2317. // cancel already logged.
  2318. hr = hrSuccess;
  2319. recip->wstrDeliveryStatus = L"250 2.1.5 %ls Ok";
  2320. } else if (hr == MAPI_W_CANCEL_MESSAGE) {
  2321. /* Loop through all remaining recipients and start responding the status to LMTP */
  2322. RespondMessageExpired(iter, listRecipients.cend());
  2323. return MAPI_W_CANCEL_MESSAGE;
  2324. } else {
  2325. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to deliver message to '%ls', error code: 0x%08X", recip->wstrUsername.c_str(), hr);
  2326. /* LMTP requires different notification when Quota for user was exceeded */
  2327. if (hr == MAPI_E_STORE_FULL)
  2328. recip->wstrDeliveryStatus = L"552 5.2.2 %ls Quota exceeded";
  2329. else
  2330. recip->wstrDeliveryStatus = L"450 4.2.0 %ls Mailbox temporarily unavailable";
  2331. }
  2332. if (lpMessageTmp) {
  2333. if (lpOrigMessage == NULL)
  2334. // If we delivered the message for the first time,
  2335. // we keep the intermediate message to make copies of.
  2336. lpMessageTmp->QueryInterface(IID_IMessage, &~lpOrigMessage);
  2337. bFallbackDelivery = bFallbackDeliveryTmp;
  2338. }
  2339. }
  2340. if (lppMessage != nullptr && lpOrigMessage)
  2341. lpOrigMessage->QueryInterface(IID_IMessage, (void**)lppMessage);
  2342. if (lpbFallbackDelivery)
  2343. *lpbFallbackDelivery = bFallbackDelivery;
  2344. return hr;
  2345. }
  2346. /**
  2347. * Commandline dagent delivery entrypoint.
  2348. * Deliver an email to one recipient.
  2349. *
  2350. * Although this function is passed a recipient list, it's only
  2351. * because the rest of the functions it calls requires this and the
  2352. * caller of this function already has a list.
  2353. *
  2354. * @param[in] lpSession User MAPI session
  2355. * @param[in] lpAdrBook Global addressbook
  2356. * @param[in] fp input file which contains the email to deliver
  2357. * @param[in] lstSingleRecip list of recipients to deliver email to (one user)
  2358. * @param[in] lpArgs delivery options
  2359. *
  2360. * @return MAPI Error code
  2361. */
  2362. static HRESULT ProcessDeliveryToSingleRecipient(pym_plugin_intf *lppyMapiPlugin,
  2363. IMAPISession *lpSession, LPADRBOOK lpAdrBook, FILE *fp,
  2364. recipients_t &lstSingleRecip, DeliveryArgs *lpArgs)
  2365. {
  2366. std::string strMail;
  2367. sc -> countInc("DAgent", "to_single_recipient");
  2368. /* Always start at the beginning of the file */
  2369. rewind(fp);
  2370. /* Read file into string */
  2371. HRESULT hr = HrMapFileToString(fp, &strMail);
  2372. if (hr != hrSuccess) {
  2373. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to map input to memory: %s (%x)",
  2374. GetMAPIErrorMessage(hr), hr);
  2375. return hr;
  2376. }
  2377. FindSpamMarker(strMail, lpArgs);
  2378. hr = ProcessDeliveryToServer(lppyMapiPlugin, lpSession, NULL, false, strMail, lpArgs->strPath, lstSingleRecip, lpAdrBook, lpArgs, NULL, NULL);
  2379. if (hr != hrSuccess)
  2380. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToSingleRecipient: ProcessDeliveryToServer failed %x", hr);
  2381. return hr;
  2382. }
  2383. /**
  2384. * Deliver email from file to a list of recipients which are grouped
  2385. * by server.
  2386. *
  2387. * @param[in] lpSession Admin MAPI Session
  2388. * @param[in] lpAdrBook Addressbook
  2389. * @param[in] fp file containing the received email
  2390. * @param[in] lpServerNameRecips recipients grouped by server
  2391. * @param[in] lpArgs delivery options
  2392. *
  2393. * @return MAPI Error code
  2394. */
  2395. static HRESULT ProcessDeliveryToCompany(pym_plugin_intf *lppyMapiPlugin,
  2396. IMAPISession *lpSession, LPADRBOOK lpAdrBook, FILE *fp,
  2397. const serverrecipients_t *lpServerNameRecips, DeliveryArgs *lpArgs)
  2398. {
  2399. HRESULT hr = hrSuccess;
  2400. object_ptr<IMessage> lpMasterMessage;
  2401. std::string strMail;
  2402. serverrecipients_t listServerPathRecips;
  2403. bool bFallbackDelivery = false;
  2404. bool bExpired = false;
  2405. sc -> countInc("DAgent", "to_company");
  2406. if (lpServerNameRecips == nullptr)
  2407. return MAPI_E_INVALID_PARAMETER;
  2408. /* Always start at the beginning of the file */
  2409. rewind(fp);
  2410. /* Read file into string */
  2411. hr = HrMapFileToString(fp, &strMail);
  2412. if (hr != hrSuccess) {
  2413. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to map input to memory: %s (%x)",
  2414. GetMAPIErrorMessage(hr), hr);
  2415. return hr;
  2416. }
  2417. FindSpamMarker(strMail, lpArgs);
  2418. hr = ResolveServerToPath(lpSession, lpServerNameRecips, lpArgs->strPath, &listServerPathRecips);
  2419. if (hr != hrSuccess) {
  2420. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToCompany(): ResolveServerToPath failed %x", hr);
  2421. return hr;
  2422. }
  2423. for (const auto &iter : listServerPathRecips) {
  2424. object_ptr<IMessage> lpMessageTmp;
  2425. bool bFallbackDeliveryTmp = false;
  2426. if (bExpired) {
  2427. /* Simply loop through all recipients to respond to LMTP */
  2428. RespondMessageExpired(iter.second.cbegin(), iter.second.cend());
  2429. continue;
  2430. }
  2431. hr = ProcessDeliveryToServer(lppyMapiPlugin, NULL,
  2432. lpMasterMessage, bFallbackDelivery, strMail,
  2433. convert_to<std::string>(iter.first), iter.second,
  2434. lpAdrBook, lpArgs, &~lpMessageTmp, &bFallbackDeliveryTmp);
  2435. if (hr == MAPI_W_CANCEL_MESSAGE)
  2436. bExpired = true;
  2437. /* Don't report the error further (ignore it) */
  2438. else if (hr != hrSuccess)
  2439. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to deliver all messages for server '%ls'",
  2440. iter.first.c_str());
  2441. /* lpMessage is our base message which we will copy to each server/recipient */
  2442. if (lpMessageTmp == nullptr)
  2443. continue;
  2444. if (lpMasterMessage == NULL)
  2445. // keep message to make copies of on the same server
  2446. lpMessageTmp->QueryInterface(IID_IMessage, &~lpMasterMessage);
  2447. bFallbackDelivery = bFallbackDeliveryTmp;
  2448. }
  2449. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Finished processing message");
  2450. return hrSuccess;
  2451. }
  2452. /**
  2453. * Within a company space, find the recipient with the lowest
  2454. * administrator rights. This user can be used to open the Global
  2455. * Addressbook.
  2456. *
  2457. * @param[in] lpServerRecips all recipients to deliver for within a company
  2458. * @param[out] lppRecipient a recipient with the rights lower than server admin
  2459. *
  2460. * @return MAPI Error code
  2461. */
  2462. static HRESULT
  2463. FindLowestAdminLevelSession(const serverrecipients_t *lpServerRecips,
  2464. DeliveryArgs *lpArgs, IMAPISession **lppUserSession)
  2465. {
  2466. HRESULT hr = hrSuccess;
  2467. ECRecipient *lpRecip = NULL;
  2468. bool bFound = false;
  2469. for (const auto &server : *lpServerRecips) {
  2470. for (const auto &recip : server.second) {
  2471. if (recip->ulDisplayType == DT_REMOTE_MAILUSER)
  2472. continue;
  2473. else if (!lpRecip)
  2474. lpRecip = recip;
  2475. else if (recip->ulAdminLevel <= lpRecip->ulAdminLevel)
  2476. lpRecip = recip;
  2477. if (lpRecip->ulAdminLevel < 2) {
  2478. // if this recipient cannot make the session for the addressbook, it will also not be able to open the store for delivery later on
  2479. hr = HrGetSession(lpArgs, lpRecip->wstrUsername.c_str(), lppUserSession);
  2480. if (hr == hrSuccess) {
  2481. bFound = true;
  2482. goto found;
  2483. }
  2484. // remove found entry, so higher admin levels can be found too if a lower cannot login
  2485. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Login on user %ls for addressbook resolves failed: 0x%08X", lpRecip->wstrUsername.c_str(), hr);
  2486. lpRecip = NULL;
  2487. }
  2488. }
  2489. }
  2490. if (lpRecip && !bFound) {
  2491. // we picked only an admin from the list, try this logon
  2492. hr = HrGetSession(lpArgs, lpRecip->wstrUsername.c_str(), lppUserSession);
  2493. bFound = (hr == hrSuccess);
  2494. }
  2495. found:
  2496. if (!bFound)
  2497. hr = MAPI_E_NOT_FOUND; /* This only happens if there are no recipients or everybody is a contact */
  2498. return hr;
  2499. }
  2500. /**
  2501. * LMTP delivery entry point
  2502. * Deliver email to a list of recipients, grouped by company, grouped by server.
  2503. *
  2504. * @param[in] lpSession Admin MAPI Session
  2505. * @param[in] fp file containing email to deliver
  2506. * @param[in] lpCompanyRecips list of all recipients to deliver to
  2507. * @param[in] lpArgs delivery options
  2508. *
  2509. * @return MAPI Error code
  2510. */
  2511. static HRESULT ProcessDeliveryToList(pym_plugin_intf *lppyMapiPlugin,
  2512. IMAPISession *lpSession, FILE *fp, companyrecipients_t *lpCompanyRecips,
  2513. DeliveryArgs *lpArgs)
  2514. {
  2515. HRESULT hr = hrSuccess;
  2516. sc -> countInc("DAgent", "to_list");
  2517. /*
  2518. * Find user with lowest adminlevel, we will use the addressbook for this
  2519. * user to make sure the recipient resolving for all recipients for the company
  2520. * resolving will occur with the minimum set of view-levels to other
  2521. * companies.
  2522. */
  2523. for (const auto &comp : *lpCompanyRecips) {
  2524. object_ptr<IMAPISession> lpUserSession;
  2525. object_ptr<IAddrBook> lpAdrBook;
  2526. hr = FindLowestAdminLevelSession(&comp.second, lpArgs, &~lpUserSession);
  2527. if (hr != hrSuccess) {
  2528. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToList(): FindLowestAdminLevelSession failed %x", hr);
  2529. return hr;
  2530. }
  2531. hr = OpenResolveAddrFolder(lpUserSession, &~lpAdrBook, nullptr);
  2532. if (hr != hrSuccess) {
  2533. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToList(): OpenResolveAddrFolder failed %x", hr);
  2534. return hr;
  2535. }
  2536. hr = ProcessDeliveryToCompany(lppyMapiPlugin, lpSession, lpAdrBook, fp, &comp.second, lpArgs);
  2537. if (hr != hrSuccess) {
  2538. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "ProcessDeliveryToList(): ProcessDeliveryToCompany failed %x", hr);
  2539. return hr;
  2540. }
  2541. }
  2542. return hrSuccess;
  2543. }
  2544. static void add_misc_headers(FILE *tmp, const std::string &helo,
  2545. const std::string &from, const DeliveryArgs *args)
  2546. {
  2547. /*
  2548. * 1. Return-Path
  2549. * Add return-path header string, as specified by RFC 5321 (ZCP-12424)
  2550. * https://tools.ietf.org/html/rfc5322
  2551. * it should look like:
  2552. * Return-Path: <noreply+dev=kopano.io@other.com>
  2553. */
  2554. fprintf(tmp, "Return-Path: <%s>\r\n", from.c_str());
  2555. /*
  2556. * 2. Received
  2557. *
  2558. * Received: from lists.digium.com (digium-69-16-138-164.phx1.puregig.net [69.16.138.164])
  2559. * by blah.com (Postfix) with ESMTP id 78BEB1CA369
  2560. * for <target@blah.com>; Mon, 12 Dec 2005 11:35:12 +0100 (CET)
  2561. */
  2562. std::string server_name;
  2563. const char *dummy = g_lpConfig->GetSetting("server_name");
  2564. if (dummy != nullptr) {
  2565. server_name = dummy;
  2566. } else {
  2567. char buffer[4096] = {0};
  2568. if (gethostname(buffer, sizeof buffer) == -1)
  2569. strcpy(buffer, "???");
  2570. server_name = buffer;
  2571. }
  2572. time_t t = time(nullptr);
  2573. struct tm *tm = localtime(&t);
  2574. char time_str[4096];
  2575. strftime(time_str, sizeof(time_str), "%a, %d %b %Y %T %z (%Z)", tm);
  2576. fprintf(tmp, "Received: from %s (%s)\r\n", helo.c_str(), args->lpChannel->peer_addr());
  2577. fprintf(tmp, "\tby %s (kopano-dagent) with LMTP;\r\n", server_name.c_str());
  2578. fprintf(tmp, "\t%s\r\n", time_str);
  2579. }
  2580. /**
  2581. * Handle an incoming LMTP connection
  2582. *
  2583. * @param[in] lpArg delivery options
  2584. *
  2585. * @return NULL
  2586. */
  2587. static void *HandlerLMTP(void *lpArg)
  2588. {
  2589. auto lpArgs = static_cast<DeliveryArgs *>(lpArg);
  2590. std::string strMailAddress;
  2591. companyrecipients_t mapRCPT;
  2592. std::list<std::string> lOrderedRecipients;
  2593. std::map<std::string, std::string> mapRecipientResults;
  2594. std::string inBuffer;
  2595. HRESULT hr = hrSuccess;
  2596. bool bLMTPQuit = false;
  2597. int timeouts = 0;
  2598. PyMapiPluginFactory pyMapiPluginFactory;
  2599. convert_context converter;
  2600. std::string curFrom = "???", heloName = "???";
  2601. LMTP lmtp(lpArgs->lpChannel, lpArgs->strPath.c_str(), g_lpConfig);
  2602. /* For resolving addresses from Address Book */
  2603. object_ptr<IMAPISession> lpSession;
  2604. object_ptr<IAddrBook> lpAdrBook;
  2605. object_ptr<IABContainer> lpAddrDir;
  2606. sc -> countInc("DAgent::LMTP", "sessions");
  2607. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Starting worker for LMTP request pid %d", getpid());
  2608. char *lpEnvGDB = getenv("GDB");
  2609. if (lpEnvGDB && parseBool(lpEnvGDB)) {
  2610. lmtp.HrResponse("220-DEBUG MODE, please wait");
  2611. Sleep(10000); //wait 10 seconds so you can attach gdb
  2612. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Starting worker for LMTP request");
  2613. }
  2614. hr = HrGetSession(lpArgs, KOPANO_SYSTEM_USER_W, &~lpSession);
  2615. if (hr != hrSuccess) {
  2616. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HandlerLMTP(): HrGetSession failed %x", hr);
  2617. lmtp.HrResponse("421 internal error: GetSession failed");
  2618. goto exit;
  2619. }
  2620. hr = OpenResolveAddrFolder(lpSession, &~lpAdrBook, &~lpAddrDir);
  2621. if (hr != hrSuccess) {
  2622. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "HandlerLMTP(): OpenResolveAddrFolder failed %x", hr);
  2623. lmtp.HrResponse("421 internal error: OpenResolveAddrFolder failed");
  2624. goto exit;
  2625. }
  2626. // Send hello message
  2627. lmtp.HrResponse("220 2.1.5 LMTP server is ready");
  2628. while (!bLMTPQuit && !g_bQuit) {
  2629. LMTP_Command eCommand;
  2630. hr = lpArgs->lpChannel->HrSelect(60);
  2631. if (hr == MAPI_E_CANCEL)
  2632. /* signalled - reevaluate quit status */
  2633. continue;
  2634. if(hr == MAPI_E_TIMEOUT) {
  2635. if(timeouts < 10) {
  2636. ++timeouts;
  2637. continue;
  2638. }
  2639. lmtp.HrResponse("221 5.0.0 Connection closed due to timeout");
  2640. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Connection closed due to timeout");
  2641. bLMTPQuit = true;
  2642. break;
  2643. } else if (hr == MAPI_E_NETWORK_ERROR) {
  2644. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Socket error: %s", strerror(errno));
  2645. bLMTPQuit = true;
  2646. break;
  2647. }
  2648. timeouts = 0;
  2649. inBuffer.clear();
  2650. errno = 0; // clear errno, might be from double logoff to server
  2651. hr = lpArgs->lpChannel->HrReadLine(&inBuffer);
  2652. if (hr != hrSuccess){
  2653. if (errno)
  2654. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to read line: %s", strerror(errno));
  2655. else
  2656. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Client disconnected");
  2657. bLMTPQuit = true;
  2658. break;
  2659. }
  2660. if (g_bQuit) {
  2661. lmtp.HrResponse("221 2.0.0 Server is shutting down");
  2662. bLMTPQuit = true;
  2663. hr = MAPI_E_CALL_FAILED;
  2664. break;
  2665. }
  2666. if (g_lpLogger->Log(EC_LOGLEVEL_DEBUG))
  2667. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "> " + inBuffer);
  2668. hr = lmtp.HrGetCommand(inBuffer, eCommand);
  2669. if (hr != hrSuccess) {
  2670. lmtp.HrResponse("555 5.5.4 Command not recognized");
  2671. sc -> countInc("DAgent::LMTP", "unknown_command");
  2672. continue;
  2673. }
  2674. switch (eCommand) {
  2675. case LMTP_Command_LHLO:
  2676. if (lmtp.HrCommandLHLO(inBuffer, heloName) == hrSuccess) {
  2677. lmtp.HrResponse("250-SERVER ready");
  2678. lmtp.HrResponse("250-PIPELINING");
  2679. lmtp.HrResponse("250-ENHANCEDSTATUSCODE");
  2680. lmtp.HrResponse("250 RSET");
  2681. } else {
  2682. lmtp.HrResponse("501 5.5.4 Syntax: LHLO hostname");
  2683. sc -> countInc("DAgent::LMTP", "LHLO_fail");
  2684. }
  2685. break;
  2686. case LMTP_Command_MAIL_FROM:
  2687. // @todo, if this command is received a second time, repond: 503 5.5.1 Error: nested MAIL command
  2688. if (lmtp.HrCommandMAILFROM(inBuffer, &curFrom) != hrSuccess) {
  2689. lmtp.HrResponse("503 5.1.7 Bad sender's mailbox address syntax");
  2690. sc -> countInc("DAgent::LMTP", "bad_sender_address");
  2691. }
  2692. else {
  2693. lmtp.HrResponse("250 2.1.0 Ok");
  2694. }
  2695. break;
  2696. case LMTP_Command_RCPT_TO: {
  2697. if (lmtp.HrCommandRCPTTO(inBuffer, &strMailAddress) != hrSuccess) {
  2698. lmtp.HrResponse("503 5.1.3 Bad destination mailbox address syntax");
  2699. sc -> countInc("DAgent::LMTP", "bad_recipient_address");
  2700. break;
  2701. }
  2702. auto lpRecipient = new ECRecipient(converter.convert_to<std::wstring>(strMailAddress));
  2703. // Resolve the mail address, so to have a user name instead of a mail address
  2704. hr = ResolveUser(lpAddrDir, lpRecipient);
  2705. if (hr == hrSuccess) {
  2706. // This is the status until it is delivered or some other error occurs
  2707. lpRecipient->wstrDeliveryStatus = L"450 4.2.0 %ls Mailbox temporarily unavailable";
  2708. hr = AddServerRecipient(&mapRCPT, &lpRecipient);
  2709. if (hr != hrSuccess)
  2710. lmtp.HrResponse("503 5.1.1 Failed to add user to recipients");
  2711. else {
  2712. // Save original order for final response when mail is delivered in DATA command
  2713. lOrderedRecipients.push_back(strMailAddress);
  2714. lmtp.HrResponse("250 2.1.5 Ok");
  2715. }
  2716. } else if (hr == MAPI_E_NOT_FOUND) {
  2717. if (lpRecipient->ulResolveFlags == MAPI_AMBIGUOUS) {
  2718. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Requested e-mail address '%s' resolves to multiple users.", strMailAddress.c_str());
  2719. lmtp.HrResponse("503 5.1.4 Destination mailbox address ambiguous");
  2720. } else {
  2721. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Requested e-mail address '%s' does not resolve to a user.", strMailAddress.c_str());
  2722. lmtp.HrResponse("503 5.1.1 User does not exist");
  2723. }
  2724. } else {
  2725. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to lookup email address, error: 0x%08X", hr);
  2726. lmtp.HrResponse("503 5.1.1 Connection error: "+stringify(hr,1));
  2727. }
  2728. /*
  2729. * If recipient resolving failed, we need to free the recipient structure,
  2730. * only when the structure was added to the mapRCPT will it be freed automatically
  2731. * later during email delivery.
  2732. */
  2733. delete lpRecipient;
  2734. break;
  2735. }
  2736. case LMTP_Command_DATA: {
  2737. if (mapRCPT.empty()) {
  2738. lmtp.HrResponse("503 5.1.1 No recipients");
  2739. sc->countInc("DAgent::LMTP", "no_recipients");
  2740. break;
  2741. }
  2742. FILE *tmp = tmpfile();
  2743. if (!tmp) {
  2744. lmtp.HrResponse("503 5.1.1 Internal error during delivery");
  2745. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to create temp file for email delivery. Please check write-access in /tmp directory. Error: %s", strerror(errno));
  2746. sc->countInc("DAgent::LMTP", "tmp_file_fail");
  2747. break;
  2748. }
  2749. add_misc_headers(tmp, heloName, curFrom, lpArgs);
  2750. hr = lmtp.HrCommandDATA(tmp);
  2751. if (hr == hrSuccess) {
  2752. std::unique_ptr<pym_plugin_intf> ptrPyMapiPlugin;
  2753. hr = pyMapiPluginFactory.create_plugin(g_lpConfig, g_lpLogger, "DAgentPluginManager", &unique_tie(ptrPyMapiPlugin));
  2754. if (hr != hrSuccess) {
  2755. ec_log_crit("K-1731: Unable to initialize the dagent plugin manager: %s (%x).",
  2756. GetMAPIErrorMessage(hr), hr);
  2757. lmtp.HrResponse("503 5.1.1 Internal error during delivery");
  2758. sc->countInc("DAgent::LMTP", "internal_error");
  2759. fclose(tmp);
  2760. hr = hrSuccess;
  2761. break;
  2762. }
  2763. // During delivery lpArgs->ulDeliveryMode can be set to DM_JUNK. However it won't reset it
  2764. // if required. So make sure to reset it here so we can safely reuse the LMTP connection
  2765. delivery_mode ulDeliveryMode = lpArgs->ulDeliveryMode;
  2766. ProcessDeliveryToList(ptrPyMapiPlugin.get(), lpSession, tmp, &mapRCPT, lpArgs);
  2767. lpArgs->ulDeliveryMode = ulDeliveryMode;
  2768. }
  2769. // We're not that interested in the error value here; if an error occurs then this will be reflected in the
  2770. // wstrDeliveryStatus of each recipient.
  2771. hr = hrSuccess;
  2772. /* Responses need to be sent in the same sequence that we received the recipients in.
  2773. * Build all responses and find the sequence through the ordered list
  2774. */
  2775. auto rawmsg = g_lpConfig->GetSetting("log_raw_message");
  2776. auto save_all = parseBool(rawmsg) && (strcasecmp(rawmsg, "all") == 0 || strcasecmp(rawmsg, "yes") == 0);
  2777. if (save_all)
  2778. SaveRawMessage(tmp, "LMTP");
  2779. for (const auto &company : mapRCPT)
  2780. for (const auto &server : company.second)
  2781. for (const auto &recip : server.second) {
  2782. WCHAR wbuffer[4096];
  2783. for (const auto i : recip->vwstrRecipients) {
  2784. swprintf(wbuffer, ARRAY_SIZE(wbuffer), recip->wstrDeliveryStatus.c_str(), i.c_str());
  2785. mapRecipientResults.insert(make_pair<std::string, std::string>(converter.convert_to<std::string>(i),
  2786. // rawsize([N]) returns N, not contents len, so cast to fix
  2787. converter.convert_to<std::string>(CHARSET_CHAR, wbuffer, rawsize(reinterpret_cast<WCHAR *>(wbuffer)), CHARSET_WCHAR)));
  2788. if (save_all)
  2789. continue;
  2790. auto save_username = converter.convert_to<std::string>(recip->wstrUsername);
  2791. SaveRawMessage(tmp, save_username.c_str());
  2792. }
  2793. }
  2794. fclose(tmp);
  2795. // Reply each recipient in the received order
  2796. for (const auto &i : lOrderedRecipients) {
  2797. std::map<std::string, std::string>::const_iterator r = mapRecipientResults.find(i);
  2798. if (r == mapRecipientResults.cend()) {
  2799. // FIXME if a following item from lORderedRecipients does succeed, then this error status
  2800. // is forgotten. is that ok? (FvH)
  2801. hr = lmtp.HrResponse("503 5.1.1 Internal error while searching recipient delivery status");
  2802. sc -> countInc("DAgent::LMTP", "internal_error");
  2803. }
  2804. else {
  2805. hr = lmtp.HrResponse(r->second);
  2806. }
  2807. if (hr != hrSuccess)
  2808. break;
  2809. }
  2810. sc->countInc("DAgent::LMTP", "received");
  2811. // Reset RCPT TO list now
  2812. FreeServerRecipients(&mapRCPT);
  2813. lOrderedRecipients.clear();
  2814. mapRecipientResults.clear();
  2815. break;
  2816. }
  2817. case LMTP_Command_RSET:
  2818. // Reset RCPT TO list
  2819. FreeServerRecipients(&mapRCPT);
  2820. lOrderedRecipients.clear();
  2821. mapRecipientResults.clear();
  2822. curFrom.clear();
  2823. lmtp.HrResponse("250 2.1.0 Ok");
  2824. break;
  2825. case LMTP_Command_QUIT:
  2826. lmtp.HrResponse("221 2.0.0 Bye");
  2827. bLMTPQuit = 1;
  2828. break;
  2829. }
  2830. }
  2831. exit:
  2832. FreeServerRecipients(&mapRCPT);
  2833. g_lpLogger->Log(EC_LOGLEVEL_INFO, "LMTP thread exiting");
  2834. delete lpArgs;
  2835. return NULL;
  2836. }
  2837. /**
  2838. * Runs the LMTP service daemon. Listens on the LMTP port for incoming
  2839. * connections and starts a new thread or child process to handle the
  2840. * connection. Only accepts the incoming connection when the maximum
  2841. * number of processes hasn't been reached.
  2842. *
  2843. * @param[in] servicename Name of the service, used to create a Unix pidfile.
  2844. * @param[in] bDaemonize Starts a forked process in this loop to run in the background if true.
  2845. * @param[in] lpArgs Struct containing delivery parameters
  2846. * @retval MAPI error code
  2847. */
  2848. static HRESULT running_service(const char *servicename, bool bDaemonize,
  2849. DeliveryArgs *lpArgs)
  2850. {
  2851. HRESULT hr = hrSuccess;
  2852. int ulListenLMTP = 0;
  2853. int err = 0;
  2854. unsigned int nMaxThreads;
  2855. int nCloseFDs = 0, pCloseFDs[1] = {0};
  2856. struct pollfd pollfd;
  2857. stack_t st;
  2858. struct sigaction act;
  2859. memset(&st, 0, sizeof(st));
  2860. memset(&act, 0, sizeof(act));
  2861. nMaxThreads = atoui(g_lpConfig->GetSetting("lmtp_max_threads"));
  2862. if (nMaxThreads == 0 || nMaxThreads == INT_MAX)
  2863. nMaxThreads = 20;
  2864. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Maximum LMTP threads set to %d", nMaxThreads);
  2865. // Setup sockets
  2866. hr = HrListen(g_lpConfig->GetSetting("server_bind"),
  2867. atoi(g_lpConfig->GetSetting("lmtp_port")), &ulListenLMTP);
  2868. if (hr != hrSuccess) {
  2869. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "running_service(): HrListen failed %x", hr);
  2870. goto exit;
  2871. }
  2872. err = zcp_bindtodevice(ulListenLMTP,
  2873. g_lpConfig->GetSetting("server_bind_intf"));
  2874. if (err < 0) {
  2875. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "SO_BINDTODEVICE: %s",
  2876. strerror(-err));
  2877. goto exit;
  2878. }
  2879. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Listening on port %s for LMTP", g_lpConfig->GetSetting("lmtp_port"));
  2880. pCloseFDs[nCloseFDs++] = ulListenLMTP;
  2881. // Setup signals
  2882. signal(SIGTERM, sigterm);
  2883. signal(SIGINT, sigterm);
  2884. signal(SIGHUP, sighup); // logrotate
  2885. signal(SIGCHLD, sigchld);
  2886. signal(SIGPIPE, SIG_IGN);
  2887. // SIGSEGV backtrace support
  2888. st.ss_sp = malloc(65536);
  2889. st.ss_flags = 0;
  2890. st.ss_size = 65536;
  2891. act.sa_sigaction = sigsegv;
  2892. act.sa_flags = SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
  2893. sigemptyset(&act.sa_mask);
  2894. sigaltstack(&st, NULL);
  2895. sigaction(SIGSEGV, &act, NULL);
  2896. sigaction(SIGBUS, &act, NULL);
  2897. sigaction(SIGABRT, &act, NULL);
  2898. struct rlimit file_limit;
  2899. file_limit.rlim_cur = KC_DESIRED_FILEDES;
  2900. file_limit.rlim_max = KC_DESIRED_FILEDES;
  2901. if (setrlimit(RLIMIT_NOFILE, &file_limit) < 0)
  2902. ec_log_err("WARNING: setrlimit(RLIMIT_NOFILE, %d) failed, you will only be able to connect up to %d sockets. Either start the process as root, or increase user limits for open file descriptors (%s)", KC_DESIRED_FILEDES, getdtablesize(), strerror(errno));
  2903. if (parseBool(g_lpConfig->GetSetting("coredump_enabled")))
  2904. unix_coredump_enable();
  2905. // fork if needed and drop privileges as requested.
  2906. // this must be done before we do anything with pthreads
  2907. if (unix_runas(g_lpConfig))
  2908. goto exit;
  2909. if (bDaemonize && unix_daemonize(g_lpConfig))
  2910. goto exit;
  2911. if (!bDaemonize)
  2912. setsid();
  2913. unix_create_pidfile(servicename, g_lpConfig);
  2914. g_lpLogger = StartLoggerProcess(g_lpConfig, g_lpLogger); // maybe replace logger
  2915. ec_log_set(g_lpLogger);
  2916. hr = MAPIInitialize(NULL);
  2917. if (hr != hrSuccess) {
  2918. g_lpLogger->Log(EC_LOGLEVEL_FATAL, "Unable to initialize MAPI: %s (%x)",
  2919. GetMAPIErrorMessage(hr), hr);
  2920. goto exit;
  2921. }
  2922. sc = new StatsClient(g_lpLogger);
  2923. sc->startup(g_lpConfig->GetSetting("z_statsd_stats"));
  2924. g_lpLogger->Log(EC_LOGLEVEL_ALWAYS, "Starting kopano-dagent LMTP mode version " PROJECT_VERSION_DAGENT_STR " (" PROJECT_SVN_REV_STR "), pid %d", getpid());
  2925. pollfd.fd = ulListenLMTP;
  2926. pollfd.events = POLLIN | POLLRDHUP;
  2927. // Mainloop
  2928. while (!g_bQuit) {
  2929. err = poll(&pollfd, 1, 10 * 1000);
  2930. if (err < 0) {
  2931. if (errno != EINTR) {
  2932. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Socket error: %s", strerror(errno));
  2933. g_bQuit = true;
  2934. hr = MAPI_E_NETWORK_ERROR;
  2935. }
  2936. continue;
  2937. } else if (err == 0) {
  2938. continue;
  2939. }
  2940. // don't start more "threads" that lmtp_max_threads config option
  2941. if (g_nLMTPThreads == nMaxThreads) {
  2942. sc -> countInc("DAgent", "max_thread_count");
  2943. Sleep(100);
  2944. continue;
  2945. }
  2946. ++g_nLMTPThreads;
  2947. // One socket has signalled a new incoming connection
  2948. auto lpDeliveryArgs = new DeliveryArgs(*lpArgs);
  2949. if (pollfd.revents & (POLLIN | POLLRDHUP)) {
  2950. hr = HrAccept(ulListenLMTP, &lpDeliveryArgs->lpChannel);
  2951. if (hr != hrSuccess) {
  2952. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "running_service(): HrAccept failed %x", hr);
  2953. // just keep running
  2954. delete lpDeliveryArgs;
  2955. hr = hrSuccess;
  2956. continue;
  2957. }
  2958. sc -> countInc("DAgent", "incoming_session");
  2959. if (unix_fork_function(HandlerLMTP, lpDeliveryArgs, nCloseFDs, pCloseFDs) < 0)
  2960. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Can't create LMTP process.");
  2961. // just keep running
  2962. // main handler always closes information it doesn't need
  2963. delete lpDeliveryArgs;
  2964. hr = hrSuccess;
  2965. continue;
  2966. }
  2967. // should not be able to get here because of continues
  2968. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Incoming traffic was not for me?!");
  2969. }
  2970. g_lpLogger->Log(EC_LOGLEVEL_ALWAYS, "LMTP service will now exit");
  2971. // in forked mode, send all children the exit signal
  2972. signal(SIGTERM, SIG_IGN);
  2973. kill(0, SIGTERM);
  2974. // wait max 30 seconds
  2975. for (int i = 30; g_nLMTPThreads && i; --i) {
  2976. if (i % 5 == 0)
  2977. g_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Waiting for %d processes to terminate", g_nLMTPThreads);
  2978. sleep(1);
  2979. }
  2980. if (g_nLMTPThreads)
  2981. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "Forced shutdown with %d processes left", g_nLMTPThreads);
  2982. else
  2983. g_lpLogger->Log(EC_LOGLEVEL_INFO, "LMTP service shutdown complete");
  2984. MAPIUninitialize();
  2985. exit:
  2986. ECChannel::HrFreeCtx();
  2987. free(st.ss_sp);
  2988. return hr;
  2989. }
  2990. /**
  2991. * Commandline delivery for one given user.
  2992. *
  2993. * Deliver the main received from <stdin> to the user named in the
  2994. * recipient parameter. Valid recipient input is a username in current
  2995. * locale, or the emailadress of the user.
  2996. *
  2997. * @param[in] recipient Username or emailaddress of a user, in current locale.
  2998. * @param[in] bStringEmail true if we should strip everything from the @ to get the name to deliver to.
  2999. * @param[in] file Filepointer to start of email.
  3000. * @param[in] lpArgs Delivery arguments, according to given options on the commandline.
  3001. * @return MAPI Error code.
  3002. */
  3003. static HRESULT deliver_recipient(pym_plugin_intf *lppyMapiPlugin,
  3004. const char *recipient, bool bStringEmail, FILE *file,
  3005. DeliveryArgs *lpArgs)
  3006. {
  3007. HRESULT hr = hrSuccess;
  3008. object_ptr<IMAPISession> lpSession;
  3009. object_ptr<IAddrBook> lpAdrBook;
  3010. object_ptr<IABContainer> lpAddrDir;
  3011. recipients_t lRCPT;
  3012. std::string strUsername;
  3013. std::wstring strwLoginname;
  3014. FILE *fpMail = NULL;
  3015. sc -> countInc("DAgent::STDIN", "received");
  3016. /* Make sure file uses CRLF */
  3017. HRESULT hr2 = HrFileLFtoCRLF(file, &fpMail);
  3018. if (hr2 != hrSuccess) {
  3019. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Unable to convert input to CRLF format: %s (%x)",
  3020. GetMAPIErrorMessage(hr2), hr2);
  3021. fpMail = file;
  3022. }
  3023. strUsername = recipient;
  3024. if (bStringEmail)
  3025. // we have to strip off the @domainname.tld to get the username
  3026. strUsername = strUsername.substr(0, strUsername.find_first_of("@"));
  3027. ECRecipient single_recip(convert_to<std::wstring>(strUsername));
  3028. // Always try to resolve the user unless we just stripped an email address.
  3029. if (!bStringEmail) {
  3030. // only suppress error when it has no meaning (eg. delivery of Unix user to itself)
  3031. hr = HrGetSession(lpArgs, KOPANO_SYSTEM_USER_W, &~lpSession, !lpArgs->bResolveAddress);
  3032. if (hr == hrSuccess) {
  3033. hr = OpenResolveAddrFolder(lpSession, &~lpAdrBook, &~lpAddrDir);
  3034. if (hr != hrSuccess) {
  3035. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "deliver_recipient(): OpenResolveAddrFolder failed %x", hr);
  3036. goto exit;
  3037. }
  3038. hr = ResolveUser(lpAddrDir, &single_recip);
  3039. if (hr != hrSuccess) {
  3040. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "deliver_recipient(): ResolveUser failed %x", hr);
  3041. if (hr == MAPI_E_NOT_FOUND)
  3042. g_bTempfail = false;
  3043. goto exit;
  3044. }
  3045. }
  3046. else if (lpArgs->bResolveAddress) {
  3047. // Failure to open the admin session will only result in error if resolve was requested.
  3048. // Non fatal, so when config is changes the message can be delivered.
  3049. goto exit;
  3050. }
  3051. else {
  3052. // set commandline user in resolved name to deliver without resolve function
  3053. single_recip.wstrUsername = single_recip.wstrRCPT;
  3054. }
  3055. }
  3056. else {
  3057. // set commandline user in resolved name to deliver without resolve function
  3058. single_recip.wstrUsername = single_recip.wstrRCPT;
  3059. }
  3060. hr = HrGetSession(lpArgs, single_recip.wstrUsername.c_str(), &~lpSession);
  3061. if (hr != hrSuccess) {
  3062. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "deliver_recipient(): HrGetSession failed %x", hr);
  3063. if (hr == MAPI_E_LOGON_FAILED)
  3064. // This is a hard failure, two things could have happened
  3065. // * strUsername does not exist
  3066. // * user does exist, but dagent is not running with the correct SYSTEM privileges, or user doesn't have a store
  3067. // Since we cannot detect the difference, we're handling both of these situations
  3068. // as hard errors
  3069. g_bTempfail = false;
  3070. goto exit;
  3071. }
  3072. hr = OpenResolveAddrFolder(lpSession, &~lpAdrBook, &~lpAddrDir);
  3073. if (hr != hrSuccess) {
  3074. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "deliver_recipient(): OpenResolveAddrFolder failed %x", hr);
  3075. goto exit;
  3076. }
  3077. lRCPT.insert(&single_recip);
  3078. hr = ProcessDeliveryToSingleRecipient(lppyMapiPlugin, lpSession, lpAdrBook, fpMail, lRCPT, lpArgs);
  3079. // Over quota is a hard error
  3080. if (hr == MAPI_E_STORE_FULL)
  3081. g_bTempfail = false;
  3082. // Save copy of the raw message
  3083. SaveRawMessage(fpMail, recipient);
  3084. exit:
  3085. if (fpMail && fpMail != file)
  3086. fclose(fpMail);
  3087. return hr;
  3088. }
  3089. static void print_help(const char *name)
  3090. {
  3091. cout << "Usage:\n" << endl;
  3092. cout << name << " <recipient>" << endl;
  3093. cout << " [-h|--host <serverpath>] [-c|--config <configfile>] [-f|--file <email-file>]" << endl;
  3094. cout << " [-j|--junk] [-F|--folder <foldername>] [-P|--public <foldername>] [-p <separator>] [-C|--create]" << endl;
  3095. cout<< " [-d|--deamonize] [-l|--listen] [-r|--read] [-s] [-v] [-q] [-e] [-n] [-R]" << endl;
  3096. cout << endl;
  3097. cout << " <recipient> Username or e-mail address of recipient" << endl;
  3098. cout << " -f file\t read e-mail from file" << endl;
  3099. cout << " -h path\t path to connect to (e.g. file:///var/run/socket)" << endl;
  3100. cout << " -c filename\t Use configuration file (e.g. /etc/kopano/dagent.cfg)\n\t\t Default: no config file used." << endl;
  3101. cout << " -j\t\t deliver in Junkmail" << endl;
  3102. cout << " -F foldername\t deliver in a subfolder of the store. Eg. 'Inbox\\sales'" << endl;
  3103. cout << " -P foldername\t deliver in a subfolder of the public store. Eg. 'sales\\incoming'" << endl;
  3104. cout << " -p separator\t Override default path separator (\\). Eg. '-p % -F 'Inbox%dealers\\resellers'" << endl;
  3105. cout << " -C\t\t Create the subfolder if it does not exist. Default behaviour is to revert to the normal Inbox folder" << endl;
  3106. cout << endl;
  3107. cout << " -s\t\t Make DAgent silent. No errors will be printed, except when the calling parameters are wrong." << endl;
  3108. cout << " -v\t\t Make DAgent verbose. More information on processing email rules can be printed." << endl;
  3109. cout << " -q\t\t Return qmail style errors." << endl;
  3110. cout << " -e\t\t Strip email domain from storename, eg username@domain.com will deliver to 'username'." << endl;
  3111. cout << " -R\t\t Attempt to resolve the passed name. Issue an error if the resolve fails. Only one of -e and -R may be specified." << endl;
  3112. cout << " -n\t\t Use 'now' as delivery time. Otherwise, time from delivery at the mailserver will be used." << endl;
  3113. cout << " -N\t\t Do not send a new mail notification to clients looking at this inbox. (Fixes Outlook 2000 running rules too)." << endl;
  3114. cout << " -r\t\t Mark mail as read on delivery. Default: mark mail as new unread mail." << endl;
  3115. cout << " -l\t\t Run DAgent as LMTP listener" << endl;
  3116. cout << " -d\t\t Run DAgent as LMTP daemon, implicates -l. DAgent will run in the background." << endl;
  3117. cout << endl;
  3118. cout << " -a responder\t path to autoresponder (e.g. /usr/local/bin/autoresponder)" << endl;
  3119. cout << "\t\t The autoresponder is called with </path/to/autoresponder> <from> <to> <subject> <kopano-username> <messagefile>" << endl;
  3120. cout << "\t\t when the autoresponder is enabled for this user, and -j is not specified" << endl;
  3121. cout << endl;
  3122. cout << "<storename> is the name of the user where to deliver this mail." << endl;
  3123. cout << "If no file is specified with -f, it will be read from standard in." << endl;
  3124. cout << endl;
  3125. }
  3126. int main(int argc, char *argv[]) {
  3127. FILE *fp = stdin;
  3128. HRESULT hr = hrSuccess;
  3129. bool bDefaultConfigWarning = false; // Provide warning when default configuration is used
  3130. bool bExplicitConfig = false; // User added config option to commandline
  3131. bool bDaemonize = false; // The dagent is not daemonized by default
  3132. bool bListenLMTP = false; // Do not listen for LMTP by default
  3133. bool qmail = false;
  3134. int loglevel = EC_LOGLEVEL_WARNING; // normally, log warnings and up
  3135. bool strip_email = false;
  3136. bool bIgnoreUnknownConfigOptions = false;
  3137. DeliveryArgs sDeliveryArgs;
  3138. sDeliveryArgs.strPath = "";
  3139. sDeliveryArgs.strAutorespond = "/usr/sbin/kopano-autorespond";
  3140. sDeliveryArgs.bCreateFolder = false;
  3141. sDeliveryArgs.strDeliveryFolder.clear();
  3142. sDeliveryArgs.szPathSeperator = '\\';
  3143. sDeliveryArgs.bResolveAddress = false;
  3144. sDeliveryArgs.bNewmailNotify = true;
  3145. sDeliveryArgs.ulDeliveryMode = DM_STORE;
  3146. imopt_default_delivery_options(&sDeliveryArgs.sDeliveryOpts);
  3147. const char *szConfig = ECConfig::GetDefaultPath("dagent.cfg");
  3148. enum {
  3149. OPT_HELP = UCHAR_MAX + 1,
  3150. OPT_CONFIG,
  3151. OPT_JUNK,
  3152. OPT_FILE,
  3153. OPT_HOST,
  3154. OPT_DAEMONIZE,
  3155. OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS,
  3156. OPT_LISTEN,
  3157. OPT_FOLDER,
  3158. OPT_PUBLIC,
  3159. OPT_CREATE,
  3160. OPT_MARKREAD,
  3161. OPT_NEWMAIL
  3162. };
  3163. static const struct option long_options[] = {
  3164. { "help", 0, NULL, OPT_HELP }, // help text
  3165. { "config", 1, NULL, OPT_CONFIG }, // config file
  3166. { "junk", 0, NULL, OPT_JUNK }, // junk folder
  3167. { "file", 1, NULL, OPT_FILE }, // file as input
  3168. { "host", 1, NULL, OPT_HOST }, // kopano host parameter
  3169. { "daemonize",0 ,NULL,OPT_DAEMONIZE}, // daemonize and listen for LMTP
  3170. { "listen", 0, NULL, OPT_LISTEN}, // listen for LMTP
  3171. { "folder", 1, NULL, OPT_FOLDER }, // subfolder of store to deliver in
  3172. { "public", 1, NULL, OPT_PUBLIC }, // subfolder of public to deliver in
  3173. { "create", 0, NULL, OPT_CREATE }, // create subfolder if not exist
  3174. { "read", 0, NULL, OPT_MARKREAD }, // mark mail as read on delivery
  3175. { "do-not-notify", 0, NULL, OPT_NEWMAIL }, // do not send new mail notification
  3176. { "ignore-unknown-config-options", 0, NULL, OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS }, // ignore unknown settings
  3177. { NULL, 0, NULL, 0 }
  3178. };
  3179. // Default settings
  3180. static const configsetting_t lpDefaults[] = {
  3181. { "server_bind", "" },
  3182. { "server_bind_intf", "" },
  3183. { "run_as_user", "kopano" },
  3184. { "run_as_group", "kopano" },
  3185. { "pid_file", "/var/run/kopano/dagent.pid" },
  3186. { "coredump_enabled", "no" },
  3187. { "lmtp_port", "2003" },
  3188. { "lmtp_max_threads", "20" },
  3189. { "process_model", "", CONFIGSETTING_UNUSED },
  3190. { "log_method", "file" },
  3191. { "log_file", "-" },
  3192. { "log_level", "3", CONFIGSETTING_RELOADABLE },
  3193. { "log_timestamp", "0" },
  3194. { "log_buffer_size", "0" },
  3195. { "server_socket", "default:" },
  3196. { "sslkey_file", "" },
  3197. { "sslkey_pass", "", CONFIGSETTING_EXACT },
  3198. { "spam_header_name", "X-Spam-Status" },
  3199. { "spam_header_value", "Yes," },
  3200. { "log_raw_message", "no", CONFIGSETTING_RELOADABLE },
  3201. { "log_raw_message_path", "/tmp", CONFIGSETTING_RELOADABLE },
  3202. { "archive_on_delivery", "no", CONFIGSETTING_RELOADABLE },
  3203. { "mr_autoaccepter", "/usr/sbin/kopano-mr-accept", CONFIGSETTING_RELOADABLE },
  3204. { "mr_autoprocessor", "/usr/sbin/kopano-mr-process", CONFIGSETTING_RELOADABLE },
  3205. { "plugin_enabled", "yes" },
  3206. { "plugin_path", "/var/lib/kopano/dagent/plugins" },
  3207. { "plugin_manager_path", "/usr/share/kopano-dagent/python" },
  3208. { "default_charset", "us-ascii"},
  3209. { "set_rule_headers", "yes", CONFIGSETTING_RELOADABLE },
  3210. { "no_double_forward", "no", CONFIGSETTING_RELOADABLE },
  3211. { "z_statsd_stats", "/var/run/kopano/statsd.sock" },
  3212. { "tmp_path", "/tmp" },
  3213. {"forward_whitelist_domains", "*"},
  3214. {"html_safety_filter", "no"},
  3215. { NULL, NULL },
  3216. };
  3217. // @todo: check if we need to setlocale(LC_MESSAGE, "");
  3218. setlocale(LC_CTYPE, "");
  3219. if (argc < 2) {
  3220. print_help(argv[0]);
  3221. return EX_USAGE;
  3222. }
  3223. int c;
  3224. while (1) {
  3225. c = my_getopt_long_permissive(argc, argv, "c:jf:dh:a:F:P:p:qsvenCVrRlN", long_options, NULL);
  3226. if (c == -1)
  3227. break;
  3228. switch (c) {
  3229. case OPT_CONFIG:
  3230. case 'c':
  3231. szConfig = optarg;
  3232. bExplicitConfig = true;
  3233. break;
  3234. case OPT_JUNK:
  3235. case 'j': // junkmail
  3236. sDeliveryArgs.ulDeliveryMode = DM_JUNK;
  3237. break;
  3238. case OPT_FILE:
  3239. case 'f': // use file as input
  3240. fp = fopen(optarg, "rb");
  3241. if(!fp) {
  3242. cerr << "Unable to open file '" << optarg << "' for reading" << endl;
  3243. return EX_USAGE;
  3244. }
  3245. break;
  3246. case OPT_LISTEN:
  3247. case 'l':
  3248. bListenLMTP = true;
  3249. bExplicitConfig = true;
  3250. break;
  3251. case OPT_DAEMONIZE:
  3252. case 'd':
  3253. //-d the Dagent is daemonized; service LMTP over socket starts listening on port 2003
  3254. bDaemonize = true;
  3255. bListenLMTP = true;
  3256. bExplicitConfig = true;
  3257. break;
  3258. case OPT_HOST:
  3259. case 'h': // 'host' (file:///var/run/kopano/server.sock)
  3260. sDeliveryArgs.strPath = optarg;
  3261. break;
  3262. case 'a': // external autoresponder program
  3263. sDeliveryArgs.strAutorespond = optarg;
  3264. break;
  3265. case 'q': // use qmail errors
  3266. qmail = true;
  3267. break;
  3268. case 's': // silent, no logging
  3269. loglevel = EC_LOGLEVEL_NONE;
  3270. break;
  3271. case 'v': // verbose logging
  3272. if (loglevel == EC_LOGLEVEL_INFO)
  3273. loglevel = EC_LOGLEVEL_DEBUG;
  3274. else
  3275. loglevel = EC_LOGLEVEL_INFO;
  3276. break;
  3277. case 'e': // strip @bla.com from username
  3278. strip_email = true;
  3279. break;
  3280. case 'n':
  3281. sDeliveryArgs.sDeliveryOpts.use_received_date = false; // conversion will use now()
  3282. break;
  3283. case OPT_FOLDER:
  3284. case 'F':
  3285. sDeliveryArgs.strDeliveryFolder = convert_to<wstring>(optarg);
  3286. break;
  3287. case OPT_PUBLIC:
  3288. case 'P':
  3289. sDeliveryArgs.ulDeliveryMode = DM_PUBLIC;
  3290. sDeliveryArgs.strDeliveryFolder = convert_to<wstring>(optarg);
  3291. break;
  3292. case 'p':
  3293. sDeliveryArgs.szPathSeperator = optarg[0];
  3294. break;
  3295. case OPT_CREATE:
  3296. case 'C':
  3297. sDeliveryArgs.bCreateFolder = true;
  3298. break;
  3299. case OPT_MARKREAD:
  3300. case 'r':
  3301. sDeliveryArgs.sDeliveryOpts.mark_as_read = true;
  3302. break;
  3303. case OPT_NEWMAIL:
  3304. case 'N':
  3305. sDeliveryArgs.bNewmailNotify = false;
  3306. break;
  3307. case OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS:
  3308. bIgnoreUnknownConfigOptions = true;
  3309. break;
  3310. case 'V':
  3311. cout << "Product version:\t" << PROJECT_VERSION_DAGENT_STR << endl
  3312. << "File version:\t\t" << PROJECT_SVN_REV_STR << endl;
  3313. return EX_USAGE;
  3314. case 'R':
  3315. sDeliveryArgs.bResolveAddress = true;
  3316. break;
  3317. case OPT_HELP:
  3318. default:
  3319. print_help(argv[0]);
  3320. return EX_USAGE;
  3321. };
  3322. }
  3323. g_lpConfig = ECConfig::Create(lpDefaults);
  3324. if (szConfig) {
  3325. /* When LoadSettings fails, provide warning to user (but wait until we actually have the Logger) */
  3326. if (!g_lpConfig->LoadSettings(szConfig))
  3327. bDefaultConfigWarning = true;
  3328. else {
  3329. auto argidx = g_lpConfig->ParseParams(argc - optind, &argv[optind]);
  3330. if (argidx < 0)
  3331. goto exit;
  3332. if (argidx > 0)
  3333. // If one overrides the config, it is assumed that the
  3334. // config is explicit. This causes errors from
  3335. // ECConfig::ParseParams to be logged. Besides that
  3336. // it doesn't make sense to override your config if
  3337. // you don't know whats in it.
  3338. bExplicitConfig = true;
  3339. // ECConfig::ParseParams returns the index in the passed array,
  3340. // after some shuffling, where it stopped parsing. optind is
  3341. // the index where my_getopt_long_permissive stopped parsing. So
  3342. // adding argidx to optind will result in the index after all
  3343. // options are parsed.
  3344. optind += argidx;
  3345. }
  3346. }
  3347. if (!bListenLMTP && optind == argc) {
  3348. cerr << "Not enough options given, need at least the username" << endl;
  3349. return EX_USAGE;
  3350. }
  3351. if (strip_email && sDeliveryArgs.bResolveAddress) {
  3352. cerr << "You must specify either -e or -R, not both" << endl;
  3353. return EX_USAGE;
  3354. }
  3355. if (!loglevel)
  3356. g_lpLogger = new ECLogger_Null();
  3357. else
  3358. g_lpLogger = CreateLogger(g_lpConfig, argv[0], "KopanoDAgent");
  3359. ec_log_set(g_lpLogger);
  3360. if (!bExplicitConfig && loglevel)
  3361. g_lpLogger->SetLoglevel(loglevel);
  3362. /* Warn users that we are using the default configuration */
  3363. if (bDefaultConfigWarning && bExplicitConfig) {
  3364. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to open configuration file %s", szConfig);
  3365. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Continuing with defaults");
  3366. }
  3367. if ((bIgnoreUnknownConfigOptions && g_lpConfig->HasErrors()) || g_lpConfig->HasWarnings())
  3368. LogConfigErrors(g_lpConfig);
  3369. if (!TmpPath::getInstance() -> OverridePath(g_lpConfig))
  3370. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Ignoring invalid path-setting!");
  3371. /* If something went wrong, create special Logger, log message and bail out */
  3372. if (g_lpConfig->HasErrors() && bExplicitConfig) {
  3373. LogConfigErrors(g_lpConfig);
  3374. hr = E_FAIL;
  3375. goto exit;
  3376. }
  3377. /* When path wasn't provided through commandline, resolve it from config file */
  3378. if (sDeliveryArgs.strPath.empty())
  3379. sDeliveryArgs.strPath = g_lpConfig->GetSetting("server_socket");
  3380. sDeliveryArgs.strPath = GetServerUnixSocket((char*)sDeliveryArgs.strPath.c_str()); // let environment override if present
  3381. sDeliveryArgs.sDeliveryOpts.ascii_upgrade = g_lpConfig->GetSetting("default_charset");
  3382. sDeliveryArgs.sDeliveryOpts.html_safety_filter = strcasecmp(g_lpConfig->GetSetting("html_safety_filter"), "yes") == 0;
  3383. if (bListenLMTP) {
  3384. /* MAPIInitialize done inside running_service */
  3385. hr = running_service(argv[0], bDaemonize, &sDeliveryArgs);
  3386. if (hr != hrSuccess)
  3387. goto exit;
  3388. }
  3389. else {
  3390. PyMapiPluginFactory pyMapiPluginFactory;
  3391. std::unique_ptr<pym_plugin_intf> ptrPyMapiPlugin;
  3392. // log process id prefix to distinguinsh events, file logger only affected
  3393. g_lpLogger->SetLogprefix(LP_PID);
  3394. hr = MAPIInitialize(NULL);
  3395. if (hr != hrSuccess) {
  3396. g_lpLogger->Log(EC_LOGLEVEL_FATAL, "Unable to initialize MAPI: %s (%x)",
  3397. GetMAPIErrorMessage(hr), hr);
  3398. goto exit;
  3399. }
  3400. sc = new StatsClient(g_lpLogger);
  3401. sc->startup(g_lpConfig->GetSetting("z_statsd_stats"));
  3402. hr = pyMapiPluginFactory.create_plugin(g_lpConfig, g_lpLogger, "DAgentPluginmanager", &unique_tie(ptrPyMapiPlugin));
  3403. if (hr != hrSuccess) {
  3404. ec_log_crit("K-1732: Unable to initialize the dagent plugin manager: %s (%x).",
  3405. GetMAPIErrorMessage(hr), hr);
  3406. hr = MAPI_E_CALL_FAILED;
  3407. goto nonlmtpexit;
  3408. }
  3409. hr = deliver_recipient(ptrPyMapiPlugin.get(), argv[optind], strip_email, fp, &sDeliveryArgs);
  3410. if (hr != hrSuccess)
  3411. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "main(): deliver_recipient failed %x", hr);
  3412. fclose(fp);
  3413. nonlmtpexit:
  3414. MAPIUninitialize();
  3415. }
  3416. exit:
  3417. delete sc;
  3418. DeleteLogger(g_lpLogger);
  3419. delete g_lpConfig;
  3420. if (hr == hrSuccess || bListenLMTP)
  3421. return EX_OK; // 0
  3422. if (g_bTempfail)
  3423. return qmail ? 111 : EX_TEMPFAIL; // please retry again later.
  3424. return qmail ? 100 : EX_SOFTWARE; // fatal error, mail was undelivered (or Fallback delivery, but still return an error)
  3425. }