123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include "config.h"
- #include <kopano/platform.h>
- #include <new>
- #include <climits>
- #include <csignal>
- #include <poll.h>
- #include <inetmapi/inetmapi.h>
- #include <mapi.h>
- #include <mapix.h>
- #include <mapidefs.h>
- #include <mapicode.h>
- #include <kopano/mapiext.h>
- #include <mapiguid.h>
- #include <kopano/CommonUtil.h>
- #include <kopano/stringutil.h>
- #include <iostream>
- #include <cstdio>
- #include <string>
- #include <vector>
- #include <algorithm>
- #include <cstdlib>
- #include <cerrno>
- #include <kopano/ECLogger.h>
- #include <kopano/ECConfig.h>
- #include <kopano/MAPIErrors.h>
- #include <kopano/my_getopt.h>
- #include <kopano/ECChannel.h>
- #include "POP3.h"
- #include "IMAP.h"
- #include <kopano/ecversion.h>
- #include "SSLUtil.h"
- #include "TmpPath.h"
- #include <kopano/UnixUtil.h>
- #include <unicode/uclean.h>
- #include <openssl/ssl.h>
- #include <kopano/hl.hpp>
- /**
- * @defgroup gateway Gateway for IMAP and POP3
- * @{
- */
- static int daemonize = 1;
- int quit = 0;
- static bool bThreads = false;
- static const char *szPath;
- static ECLogger *g_lpLogger = NULL;
- static ECConfig *g_lpConfig = NULL;
- static pthread_t mainthread;
- static int nChildren = 0;
- static std::string g_strHostString;
- static void sigterm(int s)
- {
- quit = 1;
- }
- static void sighup(int sig)
- {
- if (bThreads && pthread_equal(pthread_self(), mainthread)==0)
- return;
- if (g_lpConfig != nullptr && !g_lpConfig->ReloadSettings() &&
- g_lpLogger != nullptr)
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to reload configuration file, continuing with current settings.");
- if (g_lpLogger == nullptr || g_lpConfig == nullptr)
- return;
- g_lpLogger->Log(EC_LOGLEVEL_INFO, "Got SIGHUP config was reloaded");
- const char *ll = g_lpConfig->GetSetting("log_level");
- int new_ll = ll ? atoi(ll) : EC_LOGLEVEL_WARNING;
- g_lpLogger->SetLoglevel(new_ll);
- bool listen_pop3s = strcmp(g_lpConfig->GetSetting("pop3s_enable"), "yes") == 0;
- bool listen_imaps = strcmp(g_lpConfig->GetSetting("imaps_enable"), "yes") == 0;
- if (listen_pop3s || listen_imaps) {
- if (ECChannel::HrSetCtx(g_lpConfig) != hrSuccess)
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Error reloading SSL context");
- else
- g_lpLogger->Log(EC_LOGLEVEL_INFO, "Reloaded SSL context");
- }
- g_lpLogger->Reset();
- g_lpLogger->Log(EC_LOGLEVEL_INFO, "Log connection was reset");
- }
- static void sigchld(int)
- {
- int stat;
- while (waitpid(-1, &stat, WNOHANG) > 0)
- --nChildren;
- }
- // SIGSEGV catcher
- static void sigsegv(int signr, siginfo_t *si, void *uc)
- {
- generic_sigsegv_handler(g_lpLogger, "Gateway",
- PROJECT_VERSION_GATEWAY_STR, signr, si, uc);
- }
- static HRESULT running_service(const char *szPath, const char *servicename);
- static void print_help(const char *name)
- {
- cout << "Usage:\n" << endl;
- cout << name << " [-F] [-h|--host <serverpath>] [-c|--config <configfile>]" << endl;
- cout << " -F\t\tDo not run in the background" << endl;
- cout << " -h path\tUse alternate connect path (e.g. file:///var/run/socket).\n\t\tDefault: file:///var/run/kopano/server.sock" << endl;
- cout << " -V Print version info." << endl;
- cout << " -c filename\tUse alternate config file (e.g. /etc/kopano-gateway.cfg)\n\t\tDefault: /etc/kopano/gateway.cfg" << endl;
- cout << endl;
- }
- enum serviceType { ST_POP3 = 0, ST_IMAP };
- struct HandlerArgs {
- serviceType type;
- ECChannel *lpChannel;
- ECLogger *lpLogger;
- ECConfig *lpConfig;
- bool bUseSSL;
- };
- static void *Handler(void *lpArg)
- {
- auto lpHandlerArgs = static_cast<HandlerArgs *>(lpArg);
- auto lpChannel = lpHandlerArgs->lpChannel;
- auto lpLogger = lpHandlerArgs->lpLogger;
- auto lpConfig = lpHandlerArgs->lpConfig;
- auto bUseSSL = lpHandlerArgs->bUseSSL;
- // szPath is global, pointing to argv variable, or lpConfig variable
- ClientProto *client;
- if (lpHandlerArgs->type == ST_POP3)
- client = new POP3(szPath, lpChannel, lpLogger, lpConfig);
- else
- client = new IMAP(szPath, lpChannel, lpLogger, lpConfig);
- // not required anymore
- delete lpHandlerArgs;
- // make sure the pipe logger does not exit when this handler exits, but only frees the memory.
- auto pipelog = dynamic_cast<ECLogger_Pipe *>(lpLogger);
- if (pipelog != nullptr)
- pipelog->Disown();
- std::string inBuffer;
- HRESULT hr;
- bool bQuit = false;
- int timeouts = 0;
- if (bUseSSL && lpChannel->HrEnableTLS() != hrSuccess) {
- lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to negotiate SSL connection");
- goto exit;
- }
- hr = client->HrSendGreeting(g_strHostString);
- if (hr != hrSuccess)
- goto exit;
- // Main command loop
- while (!bQuit && !quit) {
- // check for data
- hr = lpChannel->HrSelect(60);
- if (hr == MAPI_E_CANCEL)
- /* signalled - reevaluate bQuit */
- continue;
- if (hr == MAPI_E_TIMEOUT) {
- if (++timeouts < client->getTimeoutMinutes())
- // ignore select() timeout for 5 (POP3) or 30 (IMAP) minutes
- continue;
- // close idle first, so we don't have a race condition with the channel
- client->HrCloseConnection("BYE Connection closed because of timeout");
- lpLogger->Log(EC_LOGLEVEL_ERROR, "Connection closed because of timeout");
- bQuit = true;
- break;
- } else if (hr == MAPI_E_NETWORK_ERROR) {
- lpLogger->Log(EC_LOGLEVEL_ERROR, "Socket error: %s", strerror(errno));
- bQuit = true;
- break;
- }
- timeouts = 0;
- inBuffer.clear();
- hr = lpChannel->HrReadLine(&inBuffer);
- if (hr != hrSuccess) {
- if (errno)
- lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to read line: %s", strerror(errno));
- else
- lpLogger->Log(EC_LOGLEVEL_ERROR, "Client disconnected");
- bQuit = true;
- break;
- }
- if (quit) {
- client->HrCloseConnection("BYE server shutting down");
- hr = MAPI_E_CALL_FAILED;
- bQuit = true;
- break;
- }
- if (client->isContinue()) {
- // we asked the client for more data, do not parse the buffer, but send it "to the previous command"
- // that last part is currently only HrCmdAuthenticate(), so no difficulties here.
- // also, PLAIN is the only supported auth method.
- client->HrProcessContinue(inBuffer);
- // no matter what happens, we continue handling the connection.
- continue;
- }
- HRESULT hr = hrSuccess;
- try {
- /* Process IMAP command */
- hr = client->HrProcessCommand(inBuffer);
- } catch (const KCHL::KMAPIError &e) {
- hr = e.code();
- }
- if (hr == MAPI_E_NETWORK_ERROR) {
- lpLogger->Log(EC_LOGLEVEL_ERROR, "Connection error.");
- bQuit = true;
- }
- if (hr == MAPI_E_END_OF_SESSION) {
- lpLogger->Log(EC_LOGLEVEL_NOTICE, "Disconnecting client.");
- bQuit = true;
- }
- }
- exit:
- lpLogger->Log(EC_LOGLEVEL_NOTICE, "Client %s thread exiting", lpChannel->peer_addr());
- client->HrDone(false); // HrDone does not send an error string to the client
- delete client;
- delete lpChannel;
- if (bThreads == false) {
- g_lpLogger->Release();
- delete g_lpConfig;
- }
- /** free SSL error data **/
- ERR_remove_state(0);
- if (bThreads)
- --nChildren;
- return NULL;
- }
- int main(int argc, char *argv[]) {
- HRESULT hr = hrSuccess;
- int c = 0;
- bool bIgnoreUnknownConfigOptions = false;
- ssl_threading_setup();
- const char *szConfig = ECConfig::GetDefaultPath("gateway.cfg");
- static const configsetting_t lpDefaults[] = {
- { "server_bind", "" },
- { "run_as_user", "kopano" },
- { "run_as_group", "kopano" },
- { "pid_file", "/var/run/kopano/gateway.pid" },
- { "running_path", "/var/lib/kopano" },
- { "process_model", "fork" },
- { "coredump_enabled", "no" },
- { "pop3_enable", "yes" },
- { "pop3_port", "110" },
- { "pop3s_enable", "no" },
- { "pop3s_port", "995" },
- { "imap_enable", "yes" },
- { "imap_port", "143" },
- { "imaps_enable", "no" },
- { "imaps_port", "993" },
- { "imap_only_mailfolders", "yes", CONFIGSETTING_RELOADABLE },
- { "imap_public_folders", "yes", CONFIGSETTING_RELOADABLE },
- { "imap_capability_idle", "yes", CONFIGSETTING_RELOADABLE },
- { "imap_always_generate", "no", CONFIGSETTING_UNUSED },
- { "imap_max_fail_commands", "10", CONFIGSETTING_RELOADABLE },
- { "imap_max_messagesize", "128M", CONFIGSETTING_RELOADABLE | CONFIGSETTING_SIZE },
- { "imap_generate_utf8", "no", CONFIGSETTING_RELOADABLE },
- { "imap_expunge_on_delete", "no", CONFIGSETTING_RELOADABLE },
- { "imap_store_rfc822", "yes", CONFIGSETTING_RELOADABLE },
- { "imap_cache_folders_time_limit", "0", CONFIGSETTING_RELOADABLE },
- { "disable_plaintext_auth", "no", CONFIGSETTING_RELOADABLE },
- { "server_socket", "http://localhost:236/" },
- { "server_hostname", "" },
- { "server_hostname_greeting", "no", CONFIGSETTING_RELOADABLE },
- {"ssl_private_key_file", "/etc/kopano/gateway/privkey.pem", CONFIGSETTING_RELOADABLE},
- {"ssl_certificate_file", "/etc/kopano/gateway/cert.pem", CONFIGSETTING_RELOADABLE},
- {"ssl_verify_client", "no", CONFIGSETTING_RELOADABLE},
- {"ssl_verify_file", "", CONFIGSETTING_RELOADABLE},
- {"ssl_verify_path", "", CONFIGSETTING_RELOADABLE},
- #ifdef SSL_TXT_SSLV2
- {"ssl_protocols", "!SSLv2", CONFIGSETTING_RELOADABLE},
- #else
- {"ssl_protocols", "", CONFIGSETTING_RELOADABLE},
- #endif
- {"ssl_ciphers", "ALL:!LOW:!SSLv2:!EXP:!aNULL", CONFIGSETTING_RELOADABLE},
- {"ssl_prefer_server_ciphers", "no", CONFIGSETTING_RELOADABLE},
- { "log_method", "file" },
- { "log_file", "-" },
- { "log_level", "3", CONFIGSETTING_RELOADABLE },
- { "log_timestamp", "1" },
- { "log_buffer_size", "0" },
- { "tmp_path", "/tmp" },
- {"bypass_auth", "no"},
- {"html_safety_filter", "no"},
- { NULL, NULL },
- };
- enum {
- OPT_HELP = UCHAR_MAX + 1,
- OPT_HOST,
- OPT_CONFIG,
- OPT_FOREGROUND,
- OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS
- };
- static const struct option long_options[] = {
- {"help", 0, NULL, OPT_HELP},
- {"host", 1, NULL, OPT_HOST},
- {"config", 1, NULL, OPT_CONFIG},
- {"foreground", 1, NULL, OPT_FOREGROUND},
- { "ignore-unknown-config-options", 0, NULL, OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS },
- {NULL, 0, NULL, 0}
- };
- // Get commandline options
- while (1) {
- c = my_getopt_long_permissive(argc, argv, "c:h:iuFV", long_options, NULL);
- if (c == -1)
- break;
- switch (c) {
- case OPT_CONFIG:
- case 'c':
- szConfig = optarg;
- break;
- case OPT_HOST:
- case 'h':
- szPath = optarg;
- break;
- case 'i': // Install service
- case 'u': // Uninstall service
- break;
- case OPT_FOREGROUND:
- case 'F':
- daemonize = 0;
- break;
- case OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS:
- bIgnoreUnknownConfigOptions = true;
- break;
- case 'V':
- cout << "Product version:\t" << PROJECT_VERSION_GATEWAY_STR << endl
- << "File version:\t\t" << PROJECT_SVN_REV_STR << endl;
- return 1;
- case OPT_HELP:
- default:
- print_help(argv[0]);
- return 1;
- }
- }
- // Setup config
- g_lpConfig = ECConfig::Create(lpDefaults);
- if (!g_lpConfig->LoadSettings(szConfig) ||
- g_lpConfig->ParseParams(argc - optind, &argv[optind]) < 0 ||
- (!bIgnoreUnknownConfigOptions && g_lpConfig->HasErrors())) {
- g_lpLogger = new ECLogger_File(EC_LOGLEVEL_INFO, 0, "-", false); // create logger without a timestamp to stderr
- if (g_lpLogger == nullptr) {
- hr = MAPI_E_NOT_ENOUGH_MEMORY;
- goto exit;
- }
- ec_log_set(g_lpLogger);
- LogConfigErrors(g_lpConfig);
- hr = E_FAIL;
- goto exit;
- }
- // Setup logging
- g_lpLogger = CreateLogger(g_lpConfig, argv[0], "KopanoGateway");
- ec_log_set(g_lpLogger);
- if ((bIgnoreUnknownConfigOptions && g_lpConfig->HasErrors()) || g_lpConfig->HasWarnings())
- LogConfigErrors(g_lpConfig);
- if (!TmpPath::getInstance() -> OverridePath(g_lpConfig))
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Ignoring invalid path-setting!");
- if (parseBool(g_lpConfig->GetSetting("bypass_auth")))
- g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Gateway is started with bypass_auth=yes meaning username and password will not be checked.");
- if (strncmp(g_lpConfig->GetSetting("process_model"), "thread", strlen("thread")) == 0) {
- bThreads = true;
- g_lpLogger->SetLogprefix(LP_TID);
- }
- if (bThreads)
- mainthread = pthread_self();
- if (!szPath)
- szPath = g_lpConfig->GetSetting("server_socket");
- g_strHostString = g_lpConfig->GetSetting("server_hostname", NULL, "");
- if (g_strHostString.empty())
- g_strHostString = GetServerFQDN();
- g_strHostString = string(" on ") + g_strHostString;
- hr = running_service(szPath, argv[0]);
- exit:
- if (hr != hrSuccess)
- fprintf(stderr, "%s: Startup failed: %s (%x). Please check the logfile (%s) for details.\n",
- argv[0], GetMAPIErrorMessage(hr), hr, g_lpConfig->GetSetting("log_file"));
- ssl_threading_cleanup();
- delete g_lpConfig;
- DeleteLogger(g_lpLogger);
- return hr == hrSuccess ? 0 : 1;
- }
- static int gw_listen_on(const char *service, const char *interface,
- const char *port_str, int *fd, int *fdlist, size_t *lsize)
- {
- if (port_str == NULL) {
- ec_log_crit("No port selected for %s", service);
- return E_FAIL;
- }
- char *end = NULL;
- uint16_t port = strtoul(port_str, &end, 0);
- if (port == 0 || (end != NULL && *end != '\0')) {
- ec_log_crit("\"%s\" is not an acceptable port number", port_str);
- return E_FAIL;
- }
- HRESULT hr = HrListen(interface, port, fd);
- if (hr != hrSuccess) {
- ec_log_crit("Unable to listen on port %u", port);
- return E_FAIL;
- }
- ec_log_info("Listening on port %u for %s", port, service);
- fdlist[*lsize] = *fd;
- ++*lsize;
- return hrSuccess;
- }
- /**
- * Runs the gateway service, starting a new thread or fork child for
- * incoming connections on any configured service.
- *
- * @param[in] szPath Unused, should be removed.
- * @param[in] servicename Name of the service, used to create a Unix pidfile.
- */
- static HRESULT running_service(const char *szPath, const char *servicename)
- {
- HRESULT hr = hrSuccess;
- int ulListenPOP3 = 0, ulListenPOP3s = 0;
- int ulListenIMAP = 0, ulListenIMAPs = 0;
- bool bListenPOP3, bListenPOP3s;
- bool bListenIMAP, bListenIMAPs;
- int pCloseFDs[4] = {0};
- size_t nCloseFDs = 0;
- struct pollfd pollfd[4];
- int nfds = 0, pfd_pop3 = -1, pfd_pop3s = -1, pfd_imap = -1, pfd_imaps = -1;
- int err = 0;
- pthread_attr_t ThreadAttr;
- const char *const interface = g_lpConfig->GetSetting("server_bind");
- // SIGSEGV backtrace support
- stack_t st;
- struct sigaction act;
- memset(&st, 0, sizeof(st));
- memset(&act, 0, sizeof(act));
- if (bThreads) {
- pthread_attr_init(&ThreadAttr);
- if (pthread_attr_setdetachstate(&ThreadAttr, PTHREAD_CREATE_DETACHED) != 0) {
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Can't set thread attribute to detached");
- goto exit;
- }
- // 1Mb of stack space per thread
- if (pthread_attr_setstacksize(&ThreadAttr, 1024 * 1024)) {
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Can't set thread stack size to 1Mb");
- goto exit;
- }
- }
- bListenPOP3 = (strcmp(g_lpConfig->GetSetting("pop3_enable"), "yes") == 0);
- bListenPOP3s = (strcmp(g_lpConfig->GetSetting("pop3s_enable"), "yes") == 0);
- bListenIMAP = (strcmp(g_lpConfig->GetSetting("imap_enable"), "yes") == 0);
- bListenIMAPs = (strcmp(g_lpConfig->GetSetting("imaps_enable"), "yes") == 0);
- // Setup SSL context
- if ((bListenPOP3s || bListenIMAPs) &&
- ECChannel::HrSetCtx(g_lpConfig) != hrSuccess) {
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Error loading SSL context, POP3S and IMAPS will be disabled");
- bListenPOP3s = false;
- bListenIMAPs = false;
- }
-
- if (!bListenPOP3 && !bListenPOP3s && !bListenIMAP && !bListenIMAPs) {
- g_lpLogger->Log(EC_LOGLEVEL_FATAL, "POP3, POP3S, IMAP and IMAPS are all four disabled");
- hr = E_FAIL;
- goto exit;
- }
- // Setup sockets
- if (bListenPOP3) {
- hr = gw_listen_on("pop3", interface, g_lpConfig->GetSetting("pop3_port"),
- &ulListenPOP3, pCloseFDs, &nCloseFDs);
- if (hr != hrSuccess)
- goto exit;
- }
- if (bListenPOP3s) {
- hr = gw_listen_on("pop3s", interface, g_lpConfig->GetSetting("pop3s_port"),
- &ulListenPOP3s, pCloseFDs, &nCloseFDs);
- if (hr != hrSuccess)
- goto exit;
- }
- if (bListenIMAP) {
- hr = gw_listen_on("imap", interface, g_lpConfig->GetSetting("imap_port"),
- &ulListenIMAP, pCloseFDs, &nCloseFDs);
- if (hr != hrSuccess)
- goto exit;
- }
- if (bListenIMAPs) {
- hr = gw_listen_on("imaps", interface, g_lpConfig->GetSetting("imaps_port"),
- &ulListenIMAPs, pCloseFDs, &nCloseFDs);
- if (hr != hrSuccess)
- goto exit;
- }
- // Setup signals
- signal(SIGTERM, sigterm);
- signal(SIGINT, sigterm);
- signal(SIGHUP, sighup);
- signal(SIGCHLD, sigchld);
- signal(SIGPIPE, SIG_IGN);
-
- st.ss_sp = malloc(65536);
- st.ss_flags = 0;
- st.ss_size = 65536;
-
- act.sa_sigaction = sigsegv;
- act.sa_flags = SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
- sigemptyset(&act.sa_mask);
-
- sigaltstack(&st, NULL);
- sigaction(SIGSEGV, &act, NULL);
- sigaction(SIGBUS, &act, NULL);
- sigaction(SIGABRT, &act, NULL);
- struct rlimit file_limit;
- file_limit.rlim_cur = KC_DESIRED_FILEDES;
- file_limit.rlim_max = KC_DESIRED_FILEDES;
-
- if (setrlimit(RLIMIT_NOFILE, &file_limit) < 0)
- 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());
- if (parseBool(g_lpConfig->GetSetting("coredump_enabled")))
- unix_coredump_enable();
- // fork if needed and drop privileges as requested.
- // this must be done before we do anything with pthreads
- if (unix_runas(g_lpConfig))
- goto exit;
- if (daemonize && unix_daemonize(g_lpConfig))
- goto exit;
- if (!daemonize)
- setsid();
- unix_create_pidfile(servicename, g_lpConfig);
- if (bThreads == false)
- g_lpLogger = StartLoggerProcess(g_lpConfig, g_lpLogger); // maybe replace logger
- ec_log_set(g_lpLogger);
- hr = MAPIInitialize(NULL);
- if (hr != hrSuccess) {
- g_lpLogger->Log(EC_LOGLEVEL_FATAL, "Unable to initialize MAPI: %s (%x)",
- GetMAPIErrorMessage(hr), hr);
- goto exit;
- }
- g_lpLogger->Log(EC_LOGLEVEL_ALWAYS, "Starting kopano-gateway version " PROJECT_VERSION_GATEWAY_STR " (" PROJECT_SVN_REV_STR "), pid %d", getpid());
- if (bListenPOP3) {
- pfd_pop3 = nfds;
- pollfd[nfds++].fd = ulListenPOP3;
- }
- if (bListenPOP3s) {
- pfd_pop3s = nfds;
- pollfd[nfds++].fd = ulListenPOP3s;
- }
- if (bListenIMAP) {
- pfd_imap = nfds;
- pollfd[nfds++].fd = ulListenIMAP;
- }
- if (bListenIMAPs) {
- pfd_imaps = nfds;
- pollfd[nfds++].fd = ulListenIMAPs;
- }
- for (size_t i = 0; i < nfds; ++i)
- pollfd[i].events = POLLIN | POLLRDHUP;
- // Mainloop
- while (!quit) {
- for (size_t i = 0; i < nfds; ++i)
- pollfd[i].revents = 0;
- err = poll(pollfd, nfds, 10 * 1000);
- if (err < 0) {
- if (errno != EINTR) {
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Socket error: %s", strerror(errno));
- quit = 1;
- hr = MAPI_E_NETWORK_ERROR;
- }
- continue;
- } else if (err == 0) {
- continue;
- }
- // One socket has signalled a new incoming connection
- auto lpHandlerArgs = new HandlerArgs;
- lpHandlerArgs->lpLogger = g_lpLogger;
- lpHandlerArgs->lpConfig = g_lpConfig;
- bool pop3_event = pfd_pop3 >= 0 && pollfd[pfd_pop3].revents & (POLLIN | POLLRDHUP);
- bool pop3s_event = pfd_pop3s >= 0 && pollfd[pfd_pop3s].revents & (POLLIN | POLLRDHUP);
- if (pop3_event || pop3s_event) {
- bool usessl;
- lpHandlerArgs->type = ST_POP3;
- // Incoming POP3(s) connection
- if (pop3s_event) {
- usessl = true;
- hr = HrAccept(ulListenPOP3s, &lpHandlerArgs->lpChannel);
- } else {
- usessl = false;
- hr = HrAccept(ulListenPOP3, &lpHandlerArgs->lpChannel);
- }
- if (hr != hrSuccess) {
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to accept POP3 socket connection.");
- // just keep running
- delete lpHandlerArgs;
- hr = hrSuccess;
- continue;
- }
- lpHandlerArgs->bUseSSL = usessl;
- pthread_t POP3Thread;
- const char *method = usessl ? "POP3s" : "POP3";
- const char *model = bThreads ? "thread" : "process";
- g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "Starting worker %s for %s request", model, method);
- if (bThreads) {
- if (pthread_create(&POP3Thread, &ThreadAttr, Handler, lpHandlerArgs) != 0) {
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Can't create %s %s.", method, model);
- // just keep running
- delete lpHandlerArgs->lpChannel;
- delete lpHandlerArgs;
- hr = hrSuccess;
- }
- else {
- ++nChildren;
- }
- set_thread_name(POP3Thread, "ZGateway " + std::string(method));
- }
- else {
- if (unix_fork_function(Handler, lpHandlerArgs, nCloseFDs, pCloseFDs) < 0)
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Can't create %s %s.", method, model);
- // just keep running
- else
- ++nChildren;
- // main handler always closes information it doesn't need
- delete lpHandlerArgs->lpChannel;
- delete lpHandlerArgs;
- hr = hrSuccess;
- }
- continue;
- }
- bool imap_event = pfd_imap >= 0 && pollfd[pfd_imap].revents & (POLLIN | POLLRDHUP);
- bool imaps_event = pfd_imaps >= 0 && pollfd[pfd_imaps].revents & (POLLIN | POLLRDHUP);
- if (imap_event || imaps_event) {
- bool usessl;
- lpHandlerArgs->type = ST_IMAP;
- // Incoming IMAP(s) connection
- if (imaps_event) {
- usessl = true;
- hr = HrAccept(ulListenIMAPs, &lpHandlerArgs->lpChannel);
- } else {
- usessl = false;
- hr = HrAccept(ulListenIMAP, &lpHandlerArgs->lpChannel);
- }
- if (hr != hrSuccess) {
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to accept IMAP socket connection.");
- // just keep running
- delete lpHandlerArgs;
- hr = hrSuccess;
- continue;
- }
- lpHandlerArgs->bUseSSL = usessl;
- pthread_t IMAPThread;
- const char *method = usessl ? "IMAPs" : "IMAP";
- const char *model = bThreads ? "thread" : "process";
- g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "Starting worker %s for %s request", model, method);
- if (bThreads) {
- if (pthread_create(&IMAPThread, &ThreadAttr, Handler, lpHandlerArgs) != 0) {
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Could not create %s %s.", method, model);
- // just keep running
- delete lpHandlerArgs->lpChannel;
- delete lpHandlerArgs;
- hr = hrSuccess;
- }
- else {
- ++nChildren;
- }
- set_thread_name(IMAPThread, "ZGateway " + std::string(method));
- }
- else {
- if (unix_fork_function(Handler, lpHandlerArgs, nCloseFDs, pCloseFDs) < 0)
- g_lpLogger->Log(EC_LOGLEVEL_ERROR, "Could not create %s %s.", method, model);
- // just keep running
- else
- ++nChildren;
- // main handler always closes information it doesn't need
- delete lpHandlerArgs->lpChannel;
- delete lpHandlerArgs;
- hr = hrSuccess;
- }
- continue;
- }
- // should not be able to get here because of continues
- g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Incoming traffic was not for me??");
- }
- g_lpLogger->Log(EC_LOGLEVEL_ALWAYS, "POP3/IMAP Gateway will now exit");
- // in forked mode, send all children the exit signal
- if (bThreads == false) {
- signal(SIGTERM, SIG_IGN);
- kill(0, SIGTERM);
- }
- // wait max 10 seconds (init script waits 15 seconds)
- for (int i = 10; nChildren != 0 && i != 0; --i) {
- if (i % 5 == 0)
- g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Waiting for %d processes to exit", nChildren);
- sleep(1);
- }
- if (nChildren)
- g_lpLogger->Log(EC_LOGLEVEL_WARNING, "Forced shutdown with %d processes left", nChildren);
- else
- g_lpLogger->Log(EC_LOGLEVEL_NOTICE, "POP3/IMAP Gateway shutdown complete");
- MAPIUninitialize();
- exit:
- ECChannel::HrFreeCtx();
- SSL_library_cleanup(); // Remove SSL data for the main application and other related libraries
- if (bThreads)
- pthread_attr_destroy(&ThreadAttr);
- free(st.ss_sp);
- // cleanup ICU data so valgrind is happy
- u_cleanup();
- return hr;
- }
- /** @} */
|