Gateway.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  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 "config.h"
  18. #include <kopano/platform.h>
  19. #include <new>
  20. #include <climits>
  21. #include <csignal>
  22. #include <poll.h>
  23. #include <inetmapi/inetmapi.h>
  24. #include <mapi.h>
  25. #include <mapix.h>
  26. #include <mapidefs.h>
  27. #include <mapicode.h>
  28. #include <kopano/mapiext.h>
  29. #include <mapiguid.h>
  30. #include <kopano/CommonUtil.h>
  31. #include <kopano/stringutil.h>
  32. #include <iostream>
  33. #include <cstdio>
  34. #include <string>
  35. #include <vector>
  36. #include <algorithm>
  37. #include <cstdlib>
  38. #include <cerrno>
  39. #include <kopano/ECLogger.h>
  40. #include <kopano/ECConfig.h>
  41. #include <kopano/MAPIErrors.h>
  42. #include <kopano/my_getopt.h>
  43. #include <kopano/ECChannel.h>
  44. #include "POP3.h"
  45. #include "IMAP.h"
  46. #include <kopano/ecversion.h>
  47. #include "SSLUtil.h"
  48. #include "TmpPath.h"
  49. #include <kopano/UnixUtil.h>
  50. #include <unicode/uclean.h>
  51. #include <openssl/ssl.h>
  52. #include <kopano/hl.hpp>
  53. /**
  54. * @defgroup gateway Gateway for IMAP and POP3
  55. * @{
  56. */
  57. static int daemonize = 1;
  58. int quit = 0;
  59. static bool bThreads = false;
  60. static const char *szPath;
  61. static ECLogger *g_lpLogger = NULL;
  62. static ECConfig *g_lpConfig = NULL;
  63. static pthread_t mainthread;
  64. static int nChildren = 0;
  65. static std::string g_strHostString;
  66. static void sigterm(int s)
  67. {
  68. quit = 1;
  69. }
  70. static void sighup(int sig)
  71. {
  72. if (bThreads && pthread_equal(pthread_self(), mainthread)==0)
  73. return;
  74. if (g_lpConfig != nullptr && !g_lpConfig->ReloadSettings() &&
  75. g_lpLogger != nullptr)
  76. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to reload configuration file, continuing with current settings.");
  77. if (g_lpLogger == nullptr || g_lpConfig == nullptr)
  78. return;
  79. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Got SIGHUP config was reloaded");
  80. const char *ll = g_lpConfig->GetSetting("log_level");
  81. int new_ll = ll ? atoi(ll) : EC_LOGLEVEL_WARNING;
  82. g_lpLogger->SetLoglevel(new_ll);
  83. bool listen_pop3s = strcmp(g_lpConfig->GetSetting("pop3s_enable"), "yes") == 0;
  84. bool listen_imaps = strcmp(g_lpConfig->GetSetting("imaps_enable"), "yes") == 0;
  85. if (listen_pop3s || listen_imaps) {
  86. if (ECChannel::HrSetCtx(g_lpConfig) != hrSuccess)
  87. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Error reloading SSL context");
  88. else
  89. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Reloaded SSL context");
  90. }
  91. g_lpLogger->Reset();
  92. g_lpLogger->Log(EC_LOGLEVEL_INFO, "Log connection was reset");
  93. }
  94. static void sigchld(int)
  95. {
  96. int stat;
  97. while (waitpid(-1, &stat, WNOHANG) > 0)
  98. --nChildren;
  99. }
  100. // SIGSEGV catcher
  101. static void sigsegv(int signr, siginfo_t *si, void *uc)
  102. {
  103. generic_sigsegv_handler(g_lpLogger, "Gateway",
  104. PROJECT_VERSION_GATEWAY_STR, signr, si, uc);
  105. }
  106. static HRESULT running_service(const char *szPath, const char *servicename);
  107. static void print_help(const char *name)
  108. {
  109. cout << "Usage:\n" << endl;
  110. cout << name << " [-F] [-h|--host <serverpath>] [-c|--config <configfile>]" << endl;
  111. cout << " -F\t\tDo not run in the background" << endl;
  112. cout << " -h path\tUse alternate connect path (e.g. file:///var/run/socket).\n\t\tDefault: file:///var/run/kopano/server.sock" << endl;
  113. cout << " -V Print version info." << endl;
  114. cout << " -c filename\tUse alternate config file (e.g. /etc/kopano-gateway.cfg)\n\t\tDefault: /etc/kopano/gateway.cfg" << endl;
  115. cout << endl;
  116. }
  117. enum serviceType { ST_POP3 = 0, ST_IMAP };
  118. struct HandlerArgs {
  119. serviceType type;
  120. ECChannel *lpChannel;
  121. ECLogger *lpLogger;
  122. ECConfig *lpConfig;
  123. bool bUseSSL;
  124. };
  125. static void *Handler(void *lpArg)
  126. {
  127. auto lpHandlerArgs = static_cast<HandlerArgs *>(lpArg);
  128. auto lpChannel = lpHandlerArgs->lpChannel;
  129. auto lpLogger = lpHandlerArgs->lpLogger;
  130. auto lpConfig = lpHandlerArgs->lpConfig;
  131. auto bUseSSL = lpHandlerArgs->bUseSSL;
  132. // szPath is global, pointing to argv variable, or lpConfig variable
  133. ClientProto *client;
  134. if (lpHandlerArgs->type == ST_POP3)
  135. client = new POP3(szPath, lpChannel, lpLogger, lpConfig);
  136. else
  137. client = new IMAP(szPath, lpChannel, lpLogger, lpConfig);
  138. // not required anymore
  139. delete lpHandlerArgs;
  140. // make sure the pipe logger does not exit when this handler exits, but only frees the memory.
  141. auto pipelog = dynamic_cast<ECLogger_Pipe *>(lpLogger);
  142. if (pipelog != nullptr)
  143. pipelog->Disown();
  144. std::string inBuffer;
  145. HRESULT hr;
  146. bool bQuit = false;
  147. int timeouts = 0;
  148. if (bUseSSL && lpChannel->HrEnableTLS() != hrSuccess) {
  149. lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to negotiate SSL connection");
  150. goto exit;
  151. }
  152. hr = client->HrSendGreeting(g_strHostString);
  153. if (hr != hrSuccess)
  154. goto exit;
  155. // Main command loop
  156. while (!bQuit && !quit) {
  157. // check for data
  158. hr = lpChannel->HrSelect(60);
  159. if (hr == MAPI_E_CANCEL)
  160. /* signalled - reevaluate bQuit */
  161. continue;
  162. if (hr == MAPI_E_TIMEOUT) {
  163. if (++timeouts < client->getTimeoutMinutes())
  164. // ignore select() timeout for 5 (POP3) or 30 (IMAP) minutes
  165. continue;
  166. // close idle first, so we don't have a race condition with the channel
  167. client->HrCloseConnection("BYE Connection closed because of timeout");
  168. lpLogger->Log(EC_LOGLEVEL_ERROR, "Connection closed because of timeout");
  169. bQuit = true;
  170. break;
  171. } else if (hr == MAPI_E_NETWORK_ERROR) {
  172. lpLogger->Log(EC_LOGLEVEL_ERROR, "Socket error: %s", strerror(errno));
  173. bQuit = true;
  174. break;
  175. }
  176. timeouts = 0;
  177. inBuffer.clear();
  178. hr = lpChannel->HrReadLine(&inBuffer);
  179. if (hr != hrSuccess) {
  180. if (errno)
  181. lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to read line: %s", strerror(errno));
  182. else
  183. lpLogger->Log(EC_LOGLEVEL_ERROR, "Client disconnected");
  184. bQuit = true;
  185. break;
  186. }
  187. if (quit) {
  188. client->HrCloseConnection("BYE server shutting down");
  189. hr = MAPI_E_CALL_FAILED;
  190. bQuit = true;
  191. break;
  192. }
  193. if (client->isContinue()) {
  194. // we asked the client for more data, do not parse the buffer, but send it "to the previous command"
  195. // that last part is currently only HrCmdAuthenticate(), so no difficulties here.
  196. // also, PLAIN is the only supported auth method.
  197. client->HrProcessContinue(inBuffer);
  198. // no matter what happens, we continue handling the connection.
  199. continue;
  200. }
  201. HRESULT hr = hrSuccess;
  202. try {
  203. /* Process IMAP command */
  204. hr = client->HrProcessCommand(inBuffer);
  205. } catch (const KCHL::KMAPIError &e) {
  206. hr = e.code();
  207. }
  208. if (hr == MAPI_E_NETWORK_ERROR) {
  209. lpLogger->Log(EC_LOGLEVEL_ERROR, "Connection error.");
  210. bQuit = true;
  211. }
  212. if (hr == MAPI_E_END_OF_SESSION) {
  213. lpLogger->Log(EC_LOGLEVEL_NOTICE, "Disconnecting client.");
  214. bQuit = true;
  215. }
  216. }
  217. exit:
  218. lpLogger->Log(EC_LOGLEVEL_NOTICE, "Client %s thread exiting", lpChannel->peer_addr());
  219. client->HrDone(false); // HrDone does not send an error string to the client
  220. delete client;
  221. delete lpChannel;
  222. if (bThreads == false) {
  223. g_lpLogger->Release();
  224. delete g_lpConfig;
  225. }
  226. /** free SSL error data **/
  227. ERR_remove_state(0);
  228. if (bThreads)
  229. --nChildren;
  230. return NULL;
  231. }
  232. int main(int argc, char *argv[]) {
  233. HRESULT hr = hrSuccess;
  234. int c = 0;
  235. bool bIgnoreUnknownConfigOptions = false;
  236. ssl_threading_setup();
  237. const char *szConfig = ECConfig::GetDefaultPath("gateway.cfg");
  238. static const configsetting_t lpDefaults[] = {
  239. { "server_bind", "" },
  240. { "run_as_user", "kopano" },
  241. { "run_as_group", "kopano" },
  242. { "pid_file", "/var/run/kopano/gateway.pid" },
  243. { "running_path", "/var/lib/kopano" },
  244. { "process_model", "fork" },
  245. { "coredump_enabled", "no" },
  246. { "pop3_enable", "yes" },
  247. { "pop3_port", "110" },
  248. { "pop3s_enable", "no" },
  249. { "pop3s_port", "995" },
  250. { "imap_enable", "yes" },
  251. { "imap_port", "143" },
  252. { "imaps_enable", "no" },
  253. { "imaps_port", "993" },
  254. { "imap_only_mailfolders", "yes", CONFIGSETTING_RELOADABLE },
  255. { "imap_public_folders", "yes", CONFIGSETTING_RELOADABLE },
  256. { "imap_capability_idle", "yes", CONFIGSETTING_RELOADABLE },
  257. { "imap_always_generate", "no", CONFIGSETTING_UNUSED },
  258. { "imap_max_fail_commands", "10", CONFIGSETTING_RELOADABLE },
  259. { "imap_max_messagesize", "128M", CONFIGSETTING_RELOADABLE | CONFIGSETTING_SIZE },
  260. { "imap_generate_utf8", "no", CONFIGSETTING_RELOADABLE },
  261. { "imap_expunge_on_delete", "no", CONFIGSETTING_RELOADABLE },
  262. { "imap_store_rfc822", "yes", CONFIGSETTING_RELOADABLE },
  263. { "imap_cache_folders_time_limit", "0", CONFIGSETTING_RELOADABLE },
  264. { "disable_plaintext_auth", "no", CONFIGSETTING_RELOADABLE },
  265. { "server_socket", "http://localhost:236/" },
  266. { "server_hostname", "" },
  267. { "server_hostname_greeting", "no", CONFIGSETTING_RELOADABLE },
  268. {"ssl_private_key_file", "/etc/kopano/gateway/privkey.pem", CONFIGSETTING_RELOADABLE},
  269. {"ssl_certificate_file", "/etc/kopano/gateway/cert.pem", CONFIGSETTING_RELOADABLE},
  270. {"ssl_verify_client", "no", CONFIGSETTING_RELOADABLE},
  271. {"ssl_verify_file", "", CONFIGSETTING_RELOADABLE},
  272. {"ssl_verify_path", "", CONFIGSETTING_RELOADABLE},
  273. #ifdef SSL_TXT_SSLV2
  274. {"ssl_protocols", "!SSLv2", CONFIGSETTING_RELOADABLE},
  275. #else
  276. {"ssl_protocols", "", CONFIGSETTING_RELOADABLE},
  277. #endif
  278. {"ssl_ciphers", "ALL:!LOW:!SSLv2:!EXP:!aNULL", CONFIGSETTING_RELOADABLE},
  279. {"ssl_prefer_server_ciphers", "no", CONFIGSETTING_RELOADABLE},
  280. { "log_method", "file" },
  281. { "log_file", "-" },
  282. { "log_level", "3", CONFIGSETTING_RELOADABLE },
  283. { "log_timestamp", "1" },
  284. { "log_buffer_size", "0" },
  285. { "tmp_path", "/tmp" },
  286. {"bypass_auth", "no"},
  287. {"html_safety_filter", "no"},
  288. { NULL, NULL },
  289. };
  290. enum {
  291. OPT_HELP = UCHAR_MAX + 1,
  292. OPT_HOST,
  293. OPT_CONFIG,
  294. OPT_FOREGROUND,
  295. OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS
  296. };
  297. static const struct option long_options[] = {
  298. {"help", 0, NULL, OPT_HELP},
  299. {"host", 1, NULL, OPT_HOST},
  300. {"config", 1, NULL, OPT_CONFIG},
  301. {"foreground", 1, NULL, OPT_FOREGROUND},
  302. { "ignore-unknown-config-options", 0, NULL, OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS },
  303. {NULL, 0, NULL, 0}
  304. };
  305. // Get commandline options
  306. while (1) {
  307. c = my_getopt_long_permissive(argc, argv, "c:h:iuFV", long_options, NULL);
  308. if (c == -1)
  309. break;
  310. switch (c) {
  311. case OPT_CONFIG:
  312. case 'c':
  313. szConfig = optarg;
  314. break;
  315. case OPT_HOST:
  316. case 'h':
  317. szPath = optarg;
  318. break;
  319. case 'i': // Install service
  320. case 'u': // Uninstall service
  321. break;
  322. case OPT_FOREGROUND:
  323. case 'F':
  324. daemonize = 0;
  325. break;
  326. case OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS:
  327. bIgnoreUnknownConfigOptions = true;
  328. break;
  329. case 'V':
  330. cout << "Product version:\t" << PROJECT_VERSION_GATEWAY_STR << endl
  331. << "File version:\t\t" << PROJECT_SVN_REV_STR << endl;
  332. return 1;
  333. case OPT_HELP:
  334. default:
  335. print_help(argv[0]);
  336. return 1;
  337. }
  338. }
  339. // Setup config
  340. g_lpConfig = ECConfig::Create(lpDefaults);
  341. if (!g_lpConfig->LoadSettings(szConfig) ||
  342. g_lpConfig->ParseParams(argc - optind, &argv[optind]) < 0 ||
  343. (!bIgnoreUnknownConfigOptions && g_lpConfig->HasErrors())) {
  344. g_lpLogger = new ECLogger_File(EC_LOGLEVEL_INFO, 0, "-", false); // create logger without a timestamp to stderr
  345. if (g_lpLogger == nullptr) {
  346. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  347. goto exit;
  348. }
  349. ec_log_set(g_lpLogger);
  350. LogConfigErrors(g_lpConfig);
  351. hr = E_FAIL;
  352. goto exit;
  353. }
  354. // Setup logging
  355. g_lpLogger = CreateLogger(g_lpConfig, argv[0], "KopanoGateway");
  356. ec_log_set(g_lpLogger);
  357. if ((bIgnoreUnknownConfigOptions && g_lpConfig->HasErrors()) || g_lpConfig->HasWarnings())
  358. LogConfigErrors(g_lpConfig);
  359. if (!TmpPath::getInstance() -> OverridePath(g_lpConfig))
  360. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Ignoring invalid path-setting!");
  361. if (parseBool(g_lpConfig->GetSetting("bypass_auth")))
  362. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Gateway is started with bypass_auth=yes meaning username and password will not be checked.");
  363. if (strncmp(g_lpConfig->GetSetting("process_model"), "thread", strlen("thread")) == 0) {
  364. bThreads = true;
  365. g_lpLogger->SetLogprefix(LP_TID);
  366. }
  367. if (bThreads)
  368. mainthread = pthread_self();
  369. if (!szPath)
  370. szPath = g_lpConfig->GetSetting("server_socket");
  371. g_strHostString = g_lpConfig->GetSetting("server_hostname", NULL, "");
  372. if (g_strHostString.empty())
  373. g_strHostString = GetServerFQDN();
  374. g_strHostString = string(" on ") + g_strHostString;
  375. hr = running_service(szPath, argv[0]);
  376. exit:
  377. if (hr != hrSuccess)
  378. fprintf(stderr, "%s: Startup failed: %s (%x). Please check the logfile (%s) for details.\n",
  379. argv[0], GetMAPIErrorMessage(hr), hr, g_lpConfig->GetSetting("log_file"));
  380. ssl_threading_cleanup();
  381. delete g_lpConfig;
  382. DeleteLogger(g_lpLogger);
  383. return hr == hrSuccess ? 0 : 1;
  384. }
  385. static int gw_listen_on(const char *service, const char *interface,
  386. const char *port_str, int *fd, int *fdlist, size_t *lsize)
  387. {
  388. if (port_str == NULL) {
  389. ec_log_crit("No port selected for %s", service);
  390. return E_FAIL;
  391. }
  392. char *end = NULL;
  393. uint16_t port = strtoul(port_str, &end, 0);
  394. if (port == 0 || (end != NULL && *end != '\0')) {
  395. ec_log_crit("\"%s\" is not an acceptable port number", port_str);
  396. return E_FAIL;
  397. }
  398. HRESULT hr = HrListen(interface, port, fd);
  399. if (hr != hrSuccess) {
  400. ec_log_crit("Unable to listen on port %u", port);
  401. return E_FAIL;
  402. }
  403. ec_log_info("Listening on port %u for %s", port, service);
  404. fdlist[*lsize] = *fd;
  405. ++*lsize;
  406. return hrSuccess;
  407. }
  408. /**
  409. * Runs the gateway service, starting a new thread or fork child for
  410. * incoming connections on any configured service.
  411. *
  412. * @param[in] szPath Unused, should be removed.
  413. * @param[in] servicename Name of the service, used to create a Unix pidfile.
  414. */
  415. static HRESULT running_service(const char *szPath, const char *servicename)
  416. {
  417. HRESULT hr = hrSuccess;
  418. int ulListenPOP3 = 0, ulListenPOP3s = 0;
  419. int ulListenIMAP = 0, ulListenIMAPs = 0;
  420. bool bListenPOP3, bListenPOP3s;
  421. bool bListenIMAP, bListenIMAPs;
  422. int pCloseFDs[4] = {0};
  423. size_t nCloseFDs = 0;
  424. struct pollfd pollfd[4];
  425. int nfds = 0, pfd_pop3 = -1, pfd_pop3s = -1, pfd_imap = -1, pfd_imaps = -1;
  426. int err = 0;
  427. pthread_attr_t ThreadAttr;
  428. const char *const interface = g_lpConfig->GetSetting("server_bind");
  429. // SIGSEGV backtrace support
  430. stack_t st;
  431. struct sigaction act;
  432. memset(&st, 0, sizeof(st));
  433. memset(&act, 0, sizeof(act));
  434. if (bThreads) {
  435. pthread_attr_init(&ThreadAttr);
  436. if (pthread_attr_setdetachstate(&ThreadAttr, PTHREAD_CREATE_DETACHED) != 0) {
  437. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Can't set thread attribute to detached");
  438. goto exit;
  439. }
  440. // 1Mb of stack space per thread
  441. if (pthread_attr_setstacksize(&ThreadAttr, 1024 * 1024)) {
  442. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Can't set thread stack size to 1Mb");
  443. goto exit;
  444. }
  445. }
  446. bListenPOP3 = (strcmp(g_lpConfig->GetSetting("pop3_enable"), "yes") == 0);
  447. bListenPOP3s = (strcmp(g_lpConfig->GetSetting("pop3s_enable"), "yes") == 0);
  448. bListenIMAP = (strcmp(g_lpConfig->GetSetting("imap_enable"), "yes") == 0);
  449. bListenIMAPs = (strcmp(g_lpConfig->GetSetting("imaps_enable"), "yes") == 0);
  450. // Setup SSL context
  451. if ((bListenPOP3s || bListenIMAPs) &&
  452. ECChannel::HrSetCtx(g_lpConfig) != hrSuccess) {
  453. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Error loading SSL context, POP3S and IMAPS will be disabled");
  454. bListenPOP3s = false;
  455. bListenIMAPs = false;
  456. }
  457. if (!bListenPOP3 && !bListenPOP3s && !bListenIMAP && !bListenIMAPs) {
  458. g_lpLogger->Log(EC_LOGLEVEL_FATAL, "POP3, POP3S, IMAP and IMAPS are all four disabled");
  459. hr = E_FAIL;
  460. goto exit;
  461. }
  462. // Setup sockets
  463. if (bListenPOP3) {
  464. hr = gw_listen_on("pop3", interface, g_lpConfig->GetSetting("pop3_port"),
  465. &ulListenPOP3, pCloseFDs, &nCloseFDs);
  466. if (hr != hrSuccess)
  467. goto exit;
  468. }
  469. if (bListenPOP3s) {
  470. hr = gw_listen_on("pop3s", interface, g_lpConfig->GetSetting("pop3s_port"),
  471. &ulListenPOP3s, pCloseFDs, &nCloseFDs);
  472. if (hr != hrSuccess)
  473. goto exit;
  474. }
  475. if (bListenIMAP) {
  476. hr = gw_listen_on("imap", interface, g_lpConfig->GetSetting("imap_port"),
  477. &ulListenIMAP, pCloseFDs, &nCloseFDs);
  478. if (hr != hrSuccess)
  479. goto exit;
  480. }
  481. if (bListenIMAPs) {
  482. hr = gw_listen_on("imaps", interface, g_lpConfig->GetSetting("imaps_port"),
  483. &ulListenIMAPs, pCloseFDs, &nCloseFDs);
  484. if (hr != hrSuccess)
  485. goto exit;
  486. }
  487. // Setup signals
  488. signal(SIGTERM, sigterm);
  489. signal(SIGINT, sigterm);
  490. signal(SIGHUP, sighup);
  491. signal(SIGCHLD, sigchld);
  492. signal(SIGPIPE, SIG_IGN);
  493. st.ss_sp = malloc(65536);
  494. st.ss_flags = 0;
  495. st.ss_size = 65536;
  496. act.sa_sigaction = sigsegv;
  497. act.sa_flags = SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
  498. sigemptyset(&act.sa_mask);
  499. sigaltstack(&st, NULL);
  500. sigaction(SIGSEGV, &act, NULL);
  501. sigaction(SIGBUS, &act, NULL);
  502. sigaction(SIGABRT, &act, NULL);
  503. struct rlimit file_limit;
  504. file_limit.rlim_cur = KC_DESIRED_FILEDES;
  505. file_limit.rlim_max = KC_DESIRED_FILEDES;
  506. if (setrlimit(RLIMIT_NOFILE, &file_limit) < 0)
  507. ec_log_warn("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", KC_DESIRED_FILEDES, getdtablesize());
  508. if (parseBool(g_lpConfig->GetSetting("coredump_enabled")))
  509. unix_coredump_enable();
  510. // fork if needed and drop privileges as requested.
  511. // this must be done before we do anything with pthreads
  512. if (unix_runas(g_lpConfig))
  513. goto exit;
  514. if (daemonize && unix_daemonize(g_lpConfig))
  515. goto exit;
  516. if (!daemonize)
  517. setsid();
  518. unix_create_pidfile(servicename, g_lpConfig);
  519. if (bThreads == false)
  520. g_lpLogger = StartLoggerProcess(g_lpConfig, g_lpLogger); // maybe replace logger
  521. ec_log_set(g_lpLogger);
  522. hr = MAPIInitialize(NULL);
  523. if (hr != hrSuccess) {
  524. g_lpLogger->Log(EC_LOGLEVEL_FATAL, "Unable to initialize MAPI: %s (%x)",
  525. GetMAPIErrorMessage(hr), hr);
  526. goto exit;
  527. }
  528. g_lpLogger->Log(EC_LOGLEVEL_ALWAYS, "Starting kopano-gateway version " PROJECT_VERSION_GATEWAY_STR " (" PROJECT_SVN_REV_STR "), pid %d", getpid());
  529. if (bListenPOP3) {
  530. pfd_pop3 = nfds;
  531. pollfd[nfds++].fd = ulListenPOP3;
  532. }
  533. if (bListenPOP3s) {
  534. pfd_pop3s = nfds;
  535. pollfd[nfds++].fd = ulListenPOP3s;
  536. }
  537. if (bListenIMAP) {
  538. pfd_imap = nfds;
  539. pollfd[nfds++].fd = ulListenIMAP;
  540. }
  541. if (bListenIMAPs) {
  542. pfd_imaps = nfds;
  543. pollfd[nfds++].fd = ulListenIMAPs;
  544. }
  545. for (size_t i = 0; i < nfds; ++i)
  546. pollfd[i].events = POLLIN | POLLRDHUP;
  547. // Mainloop
  548. while (!quit) {
  549. for (size_t i = 0; i < nfds; ++i)
  550. pollfd[i].revents = 0;
  551. err = poll(pollfd, nfds, 10 * 1000);
  552. if (err < 0) {
  553. if (errno != EINTR) {
  554. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Socket error: %s", strerror(errno));
  555. quit = 1;
  556. hr = MAPI_E_NETWORK_ERROR;
  557. }
  558. continue;
  559. } else if (err == 0) {
  560. continue;
  561. }
  562. // One socket has signalled a new incoming connection
  563. auto lpHandlerArgs = new HandlerArgs;
  564. lpHandlerArgs->lpLogger = g_lpLogger;
  565. lpHandlerArgs->lpConfig = g_lpConfig;
  566. bool pop3_event = pfd_pop3 >= 0 && pollfd[pfd_pop3].revents & (POLLIN | POLLRDHUP);
  567. bool pop3s_event = pfd_pop3s >= 0 && pollfd[pfd_pop3s].revents & (POLLIN | POLLRDHUP);
  568. if (pop3_event || pop3s_event) {
  569. bool usessl;
  570. lpHandlerArgs->type = ST_POP3;
  571. // Incoming POP3(s) connection
  572. if (pop3s_event) {
  573. usessl = true;
  574. hr = HrAccept(ulListenPOP3s, &lpHandlerArgs->lpChannel);
  575. } else {
  576. usessl = false;
  577. hr = HrAccept(ulListenPOP3, &lpHandlerArgs->lpChannel);
  578. }
  579. if (hr != hrSuccess) {
  580. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to accept POP3 socket connection.");
  581. // just keep running
  582. delete lpHandlerArgs;
  583. hr = hrSuccess;
  584. continue;
  585. }
  586. lpHandlerArgs->bUseSSL = usessl;
  587. pthread_t POP3Thread;
  588. const char *method = usessl ? "POP3s" : "POP3";
  589. const char *model = bThreads ? "thread" : "process";
  590. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "Starting worker %s for %s request", model, method);
  591. if (bThreads) {
  592. if (pthread_create(&POP3Thread, &ThreadAttr, Handler, lpHandlerArgs) != 0) {
  593. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Can't create %s %s.", method, model);
  594. // just keep running
  595. delete lpHandlerArgs->lpChannel;
  596. delete lpHandlerArgs;
  597. hr = hrSuccess;
  598. }
  599. else {
  600. ++nChildren;
  601. }
  602. set_thread_name(POP3Thread, "ZGateway " + std::string(method));
  603. }
  604. else {
  605. if (unix_fork_function(Handler, lpHandlerArgs, nCloseFDs, pCloseFDs) < 0)
  606. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Can't create %s %s.", method, model);
  607. // just keep running
  608. else
  609. ++nChildren;
  610. // main handler always closes information it doesn't need
  611. delete lpHandlerArgs->lpChannel;
  612. delete lpHandlerArgs;
  613. hr = hrSuccess;
  614. }
  615. continue;
  616. }
  617. bool imap_event = pfd_imap >= 0 && pollfd[pfd_imap].revents & (POLLIN | POLLRDHUP);
  618. bool imaps_event = pfd_imaps >= 0 && pollfd[pfd_imaps].revents & (POLLIN | POLLRDHUP);
  619. if (imap_event || imaps_event) {
  620. bool usessl;
  621. lpHandlerArgs->type = ST_IMAP;
  622. // Incoming IMAP(s) connection
  623. if (imaps_event) {
  624. usessl = true;
  625. hr = HrAccept(ulListenIMAPs, &lpHandlerArgs->lpChannel);
  626. } else {
  627. usessl = false;
  628. hr = HrAccept(ulListenIMAP, &lpHandlerArgs->lpChannel);
  629. }
  630. if (hr != hrSuccess) {
  631. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to accept IMAP socket connection.");
  632. // just keep running
  633. delete lpHandlerArgs;
  634. hr = hrSuccess;
  635. continue;
  636. }
  637. lpHandlerArgs->bUseSSL = usessl;
  638. pthread_t IMAPThread;
  639. const char *method = usessl ? "IMAPs" : "IMAP";
  640. const char *model = bThreads ? "thread" : "process";
  641. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "Starting worker %s for %s request", model, method);
  642. if (bThreads) {
  643. if (pthread_create(&IMAPThread, &ThreadAttr, Handler, lpHandlerArgs) != 0) {
  644. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Could not create %s %s.", method, model);
  645. // just keep running
  646. delete lpHandlerArgs->lpChannel;
  647. delete lpHandlerArgs;
  648. hr = hrSuccess;
  649. }
  650. else {
  651. ++nChildren;
  652. }
  653. set_thread_name(IMAPThread, "ZGateway " + std::string(method));
  654. }
  655. else {
  656. if (unix_fork_function(Handler, lpHandlerArgs, nCloseFDs, pCloseFDs) < 0)
  657. g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Could not create %s %s.", method, model);
  658. // just keep running
  659. else
  660. ++nChildren;
  661. // main handler always closes information it doesn't need
  662. delete lpHandlerArgs->lpChannel;
  663. delete lpHandlerArgs;
  664. hr = hrSuccess;
  665. }
  666. continue;
  667. }
  668. // should not be able to get here because of continues
  669. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Incoming traffic was not for me??");
  670. }
  671. g_lpLogger->Log(EC_LOGLEVEL_ALWAYS, "POP3/IMAP Gateway will now exit");
  672. // in forked mode, send all children the exit signal
  673. if (bThreads == false) {
  674. signal(SIGTERM, SIG_IGN);
  675. kill(0, SIGTERM);
  676. }
  677. // wait max 10 seconds (init script waits 15 seconds)
  678. for (int i = 10; nChildren != 0 && i != 0; --i) {
  679. if (i % 5 == 0)
  680. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Waiting for %d processes to exit", nChildren);
  681. sleep(1);
  682. }
  683. if (nChildren)
  684. g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Forced shutdown with %d processes left", nChildren);
  685. else
  686. g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "POP3/IMAP Gateway shutdown complete");
  687. MAPIUninitialize();
  688. exit:
  689. ECChannel::HrFreeCtx();
  690. SSL_library_cleanup(); // Remove SSL data for the main application and other related libraries
  691. if (bThreads)
  692. pthread_attr_destroy(&ThreadAttr);
  693. free(st.ss_sp);
  694. // cleanup ICU data so valgrind is happy
  695. u_cleanup();
  696. return hr;
  697. }
  698. /** @} */