LMTP.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/platform.h>
  18. #include <cstdio>
  19. #include <cstdlib>
  20. #include <iostream>
  21. #include <cctype>
  22. #include <algorithm>
  23. #include <mapi.h>
  24. #include <mapix.h>
  25. #include <mapicode.h>
  26. #include <mapidefs.h>
  27. #include <mapiutil.h>
  28. #include <inetmapi/inetmapi.h>
  29. #include <kopano/mapiext.h>
  30. #include <kopano/CommonUtil.h>
  31. #include <kopano/MAPIErrors.h>
  32. #include <fileutil.h>
  33. #include <kopano/ECTags.h>
  34. #include <kopano/ECChannel.h>
  35. #include "LMTP.h"
  36. #include <kopano/stringutil.h>
  37. #include "fileutil.h"
  38. using namespace std;
  39. LMTP::LMTP(ECChannel *lpChan, const char *szServerPath, ECConfig *lpConf)
  40. {
  41. m_lpChannel = lpChan;
  42. m_lpConfig = lpConf;
  43. m_strPath = szServerPath;
  44. }
  45. /**
  46. * Tests the start of the input for the LMTP command. LMTP is case
  47. * insensitive.
  48. * LMTP commands are:
  49. * @arg LHLO
  50. * @arg MAIL FROM:
  51. * @arg RCPT TO:
  52. * @arg DATA
  53. * @arg RSET
  54. * @arg QUIT
  55. *
  56. * @param[in] strCommand The received line from the LMTP client
  57. * @param[out] eCommand enum describing the received command
  58. *
  59. * @return MAPI error code
  60. * @retval MAPI_E_CALL_FAILED unknown or unsupported command received
  61. */
  62. HRESULT LMTP::HrGetCommand(const string &strCommand, LMTP_Command &eCommand)
  63. {
  64. HRESULT hr = hrSuccess;
  65. if (strncasecmp(strCommand.c_str(), "LHLO", strlen("LHLO")) == 0)
  66. eCommand = LMTP_Command_LHLO;
  67. else if (strncasecmp(strCommand.c_str(), "MAIL FROM:", strlen("MAIL FROM:")) == 0)
  68. eCommand = LMTP_Command_MAIL_FROM;
  69. else if (strncasecmp(strCommand.c_str(), "RCPT TO:", strlen("RCPT TO:")) == 0)
  70. eCommand = LMTP_Command_RCPT_TO;
  71. else if (strncasecmp(strCommand.c_str(), "DATA", strlen("DATA")) == 0)
  72. eCommand = LMTP_Command_DATA;
  73. else if (strncasecmp(strCommand.c_str(), "RSET", strlen("RSET")) == 0)
  74. eCommand = LMTP_Command_RSET;
  75. else if (strncasecmp(strCommand.c_str(), "QUIT", strlen("QUIT")) == 0)
  76. eCommand = LMTP_Command_QUIT;
  77. else
  78. hr = MAPI_E_CALL_FAILED;
  79. return hr;
  80. }
  81. /**
  82. * Send the following response to the LMTP client.
  83. *
  84. * @param[in] strResponse String to send
  85. *
  86. * @return Possible error during write to the client
  87. */
  88. HRESULT LMTP::HrResponse(const string &strResponse)
  89. {
  90. HRESULT hr;
  91. ec_log_debug("< %s", strResponse.c_str());
  92. hr = m_lpChannel->HrWriteLine(strResponse);
  93. if (hr != hrSuccess)
  94. ec_log_err("LMTP write error: %s (%x)",
  95. GetMAPIErrorMessage(hr), hr);
  96. return hr;
  97. }
  98. /**
  99. * Parse the received string for a valid LHLO command.
  100. *
  101. * @param[in] strInput the full LHLO command received
  102. *
  103. * @return always hrSuccess
  104. */
  105. HRESULT LMTP::HrCommandLHLO(const string &strInput, string & nameOut)
  106. {
  107. size_t pos = strInput.find(' ');
  108. nameOut.assign(strInput.c_str() + pos + 1);
  109. // Input definitly starts with LHLO
  110. // use HrResponse("501 5.5.4 Syntax: LHLO hostname"); in case of error, but we don't.
  111. ec_log_debug("LHLO ID: %s", nameOut.c_str());
  112. return hrSuccess;
  113. }
  114. /**
  115. * Parse the received string for a valid MAIL FROM: command.
  116. * The correct syntax for the MAIL FROM is:
  117. * MAIL FROM:<email@address.domain>
  118. *
  119. * However, it's possible extra spaces are added in the string, and we
  120. * should correctly accept this to deliver the mail.
  121. * We ignore the contents from the address, and use the From: header.
  122. *
  123. * @param[in] strFrom the full MAIL FROM command
  124. *
  125. * @return MAPI error code
  126. * @retval MAPI_E_NOT_FOUND < or > character was not found: this is fatal.
  127. */
  128. HRESULT LMTP::HrCommandMAILFROM(const string &strFrom, std::string *const strAddress)
  129. {
  130. // strFrom is only checked for syntax
  131. return HrParseAddress(strFrom, strAddress);
  132. }
  133. /**
  134. * Parse the received string for a valid RCPT TO: command.
  135. *
  136. * @param[in] strTo the full RCPT TO command
  137. * @param[out] strUnresolved the parsed email address from the command, user will be resolved by DAgent.
  138. *
  139. * @return MAPI error code
  140. * @retval MAPI_E_NOT_FOUND < or > character was not found: this is fatal.
  141. */
  142. HRESULT LMTP::HrCommandRCPTTO(const string &strTo, string *strUnresolved)
  143. {
  144. HRESULT hr = HrParseAddress(strTo, strUnresolved);
  145. if (hr == hrSuccess)
  146. ec_log_debug("Resolved command \"%s\" to recipient address \"%s\"",
  147. strTo.c_str(), strUnresolved->c_str());
  148. else
  149. ec_log_err("Invalid recipient address in command \"%s\": %s (%x)",
  150. strTo.c_str(), GetMAPIErrorMessage(hr), hr);
  151. return hr;
  152. }
  153. /**
  154. * Receive the DATA from the client and save to a file using \r\n
  155. * enters. This file will be mmap()ed by the DAgent.
  156. *
  157. * @param[in] tmp a FILE pointer to a temporary file with write access
  158. *
  159. * @return MAPI error code, read/write errors from client.
  160. */
  161. HRESULT LMTP::HrCommandDATA(FILE *tmp)
  162. {
  163. HRESULT hr;
  164. std::string inBuffer;
  165. std::string message;
  166. int offset;
  167. ssize_t ret, to_write;
  168. hr = HrResponse("354 2.1.5 Start mail input; end with <CRLF>.<CRLF>");
  169. if (hr != hrSuccess) {
  170. ec_log_err("Error during DATA communication with client: %s (%x).",
  171. GetMAPIErrorMessage(hr), hr);
  172. return hr;
  173. }
  174. // Now the mail body needs to be read line by line until <CRLF>.<CRLF> is encountered
  175. while (1) {
  176. hr = m_lpChannel->HrReadLine(&inBuffer);
  177. if (hr != hrSuccess) {
  178. ec_log_err("Error during DATA communication with client: %s (%x).",
  179. GetMAPIErrorMessage(hr), hr);
  180. return hr;
  181. }
  182. if (inBuffer == ".")
  183. break;
  184. offset = 0;
  185. if (inBuffer[0] == '.')
  186. offset = 1; // "remove" escape point, since it wasn't the end of mail marker
  187. to_write = inBuffer.size() - offset;
  188. ret = fwrite((char *)inBuffer.c_str() + offset, 1, to_write, tmp);
  189. if (ret != to_write) {
  190. ec_log_err("Error during DATA communication with client: %s", strerror(errno));
  191. return MAPI_E_FAILURE;
  192. }
  193. // The data from HrReadLine does not contain the CRLF, so add that here
  194. if (fwrite("\r\n", 1, 2, tmp) != 2) {
  195. ec_log_err("Error during DATA communication with client: %s", strerror(errno));
  196. return MAPI_E_FAILURE;
  197. }
  198. message += inBuffer + "\r\n";
  199. }
  200. #if 0
  201. if (m_lpLogger->Log(EC_LOGLEVEL_DEBUG + 1)) // really hidden output (limited to 10k in logger)
  202. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Received message:\n" + message);
  203. #endif
  204. return hrSuccess;
  205. }
  206. /**
  207. * Handle the very difficult QUIT command.
  208. *
  209. * @return always hrSuccess
  210. */
  211. HRESULT LMTP::HrCommandQUIT()
  212. {
  213. return hrSuccess;
  214. }
  215. /**
  216. * Parse an address given in a MAIL FROM or RCPT TO command.
  217. *
  218. * @param[in] strInput a full MAIL FROM or RCPT TO command
  219. * @param[out] strAddress the address found in the command
  220. *
  221. * @return MAPI error code
  222. * @retval MAPI_E_NOT_FOUND mandatory < or > not found in command.
  223. */
  224. HRESULT LMTP::HrParseAddress(const std::string &strInput, std::string *strAddress)
  225. {
  226. std::string strAddr;
  227. size_t pos1;
  228. size_t pos2;
  229. pos1 = strInput.find('<');
  230. pos2 = strInput.find('>', pos1);
  231. if (pos1 == std::string::npos || pos2 == std::string::npos)
  232. return MAPI_E_NOT_FOUND;
  233. strAddr = strInput.substr(pos1+1, pos2-pos1-1);
  234. trim(strAddr);
  235. strAddress->swap(strAddr);
  236. return hrSuccess;
  237. }