CalDAV.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  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 <memory>
  20. #include <new>
  21. #include <type_traits>
  22. #include <climits>
  23. #include <poll.h>
  24. #include "mapidefs.h"
  25. #include <mapix.h>
  26. #include <kopano/MAPIErrors.h>
  27. #include "Http.h"
  28. #include "CalDavUtil.h"
  29. #include "iCal.h"
  30. #include "WebDav.h"
  31. #include "CalDavProto.h"
  32. #include "ProtocolBase.h"
  33. #include <csignal>
  34. #include <iostream>
  35. #include <string>
  36. #include <kopano/ECLogger.h>
  37. #include <kopano/ECChannel.h>
  38. #include <kopano/memory.hpp>
  39. #include <kopano/my_getopt.h>
  40. #include <kopano/ecversion.h>
  41. #include <kopano/CommonUtil.h>
  42. #include "SSLUtil.h"
  43. #include "TmpPath.h"
  44. using namespace std;
  45. #include <execinfo.h>
  46. #include <kopano/UnixUtil.h>
  47. #include <unicode/uclean.h>
  48. #include <openssl/ssl.h>
  49. struct HandlerArgs {
  50. ECChannel *lpChannel;
  51. bool bUseSSL;
  52. };
  53. static bool g_bDaemonize = true;
  54. static bool g_bQuit = false;
  55. static bool g_bThreads = false;
  56. static ECLogger *g_lpLogger = NULL;
  57. static ECConfig *g_lpConfig = NULL;
  58. static pthread_t mainthread;
  59. static int nChildren = 0;
  60. static HRESULT HrSetupListeners(int *lpulNormalSocket, int *lpulSecureSocket);
  61. static HRESULT HrProcessConnections(int ulNormalSocket, int ulSecureSocket);
  62. static HRESULT HrStartHandlerClient(ECChannel *lpChannel, bool bUseSSL, int nCloseFDs, int *pCloseFDs);
  63. static void *HandlerClient(void *lpArg);
  64. static HRESULT HrHandleRequest(ECChannel *lpChannel);
  65. #define KEEP_ALIVE_TIME 300
  66. static void sigterm(int)
  67. {
  68. g_bQuit = true;
  69. }
  70. static void sighup(int)
  71. {
  72. if (g_bThreads && pthread_equal(pthread_self(), mainthread)==0)
  73. return;
  74. if (g_lpConfig != nullptr && !g_lpConfig->ReloadSettings() &&
  75. g_lpLogger != nullptr)
  76. ec_log_crit("Unable to reload configuration file, continuing with current settings.");
  77. if (g_lpLogger) {
  78. if (g_lpConfig) {
  79. const char *ll = g_lpConfig->GetSetting("log_level");
  80. int new_ll = ll ? atoi(ll) : EC_LOGLEVEL_WARNING;
  81. g_lpLogger->SetLoglevel(new_ll);
  82. }
  83. g_lpLogger->Reset();
  84. ec_log_warn("Log connection was reset");
  85. }
  86. }
  87. static void sigchld(int)
  88. {
  89. int stat;
  90. while (waitpid (-1, &stat, WNOHANG) > 0)
  91. --nChildren;
  92. }
  93. static void sigsegv(int signr, siginfo_t *si, void *uc)
  94. {
  95. generic_sigsegv_handler(g_lpLogger, "CalDAV",
  96. PROJECT_VERSION_GATEWAY_STR, signr, si, uc);
  97. }
  98. static void PrintHelp(const char *name)
  99. {
  100. cout << "Usage:\n" << endl;
  101. cout << name << " [-h] [-F] [-V] [-c <configfile>]" << endl;
  102. cout << " -F\t\tDo not run in the background" << endl;
  103. cout << " -h\t\tShows this help." << endl;
  104. cout << " -V\t\tPrint version info." << endl;
  105. cout << " -c filename\tUse alternate config file (e.g. /etc/kopano/ical.cfg)\n\t\tDefault: /etc/kopano/ical.cfg" << endl;
  106. cout << endl;
  107. }
  108. static void PrintVersion(void)
  109. {
  110. cout << "Product version:\t" << PROJECT_VERSION_CALDAV_STR << endl << "File version:\t\t" << PROJECT_SVN_REV_STR << endl;
  111. }
  112. int main(int argc, char **argv) {
  113. HRESULT hr = hrSuccess;
  114. int ulListenCalDAV = 0;
  115. int ulListenCalDAVs = 0;
  116. bool bIgnoreUnknownConfigOptions = false;
  117. stack_t st = {0};
  118. struct sigaction act;
  119. // Configuration
  120. int opt = 0;
  121. const char *lpszCfg = ECConfig::GetDefaultPath("ical.cfg");
  122. static const configsetting_t lpDefaults[] = {
  123. { "run_as_user", "kopano" },
  124. { "run_as_group", "kopano" },
  125. { "pid_file", "/var/run/kopano/ical.pid" },
  126. { "running_path", "/var/lib/kopano" },
  127. { "process_model", "fork" },
  128. { "server_bind", "" },
  129. { "ical_port", "8080" },
  130. { "ical_enable", "yes" },
  131. { "icals_port", "8443" },
  132. { "icals_enable", "no" },
  133. { "enable_ical_get", "yes", CONFIGSETTING_RELOADABLE },
  134. { "server_socket", "http://localhost:236/" },
  135. { "server_timezone","Europe/Amsterdam"},
  136. { "default_charset","utf-8"},
  137. { "log_method", "file" },
  138. { "log_file", "/var/log/kopano/ical.log" },
  139. { "log_level", "3", CONFIGSETTING_RELOADABLE },
  140. { "log_timestamp", "1" },
  141. { "log_buffer_size", "0" },
  142. { "ssl_private_key_file", "/etc/kopano/ical/privkey.pem" },
  143. { "ssl_certificate_file", "/etc/kopano/ical/cert.pem" },
  144. #ifdef SSL_TXT_SSLV2
  145. { "ssl_protocols", "!SSLv2" },
  146. #else
  147. {"ssl_protocols", ""},
  148. #endif
  149. { "ssl_ciphers", "ALL:!LOW:!SSLv2:!EXP:!aNULL" },
  150. { "ssl_prefer_server_ciphers", "no" },
  151. { "ssl_verify_client", "no" },
  152. { "ssl_verify_file", "" },
  153. { "ssl_verify_path", "" },
  154. { "tmp_path", "/tmp" },
  155. { NULL, NULL },
  156. };
  157. enum {
  158. OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS = UCHAR_MAX + 1,
  159. };
  160. static const struct option long_options[] = {
  161. {"help", no_argument, NULL, 'h'},
  162. {"config", required_argument, NULL, 'c'},
  163. {"version", no_argument, NULL, 'v'},
  164. {"foreground", no_argument, NULL, 'F'},
  165. {"ignore-unknown-config-options", 0, NULL, OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS },
  166. {NULL, 0, NULL, 0}
  167. };
  168. setlocale(LC_CTYPE, "");
  169. while (1) {
  170. opt = my_getopt_long_permissive(argc, argv, "Fhc:V", long_options, NULL);
  171. if (opt == -1)
  172. break;
  173. switch (opt) {
  174. case 'c':
  175. lpszCfg = optarg;
  176. break;
  177. case 'F':
  178. g_bDaemonize = false;
  179. break;
  180. case OPT_IGNORE_UNKNOWN_CONFIG_OPTIONS:
  181. bIgnoreUnknownConfigOptions = true;
  182. break;
  183. case 'V':
  184. PrintVersion();
  185. goto exit;
  186. case 'h':
  187. default:
  188. PrintHelp(argv[0]);
  189. goto exit;
  190. }
  191. }
  192. // init xml parser
  193. xmlInitParser();
  194. g_lpConfig = ECConfig::Create(lpDefaults);
  195. if (!g_lpConfig->LoadSettings(lpszCfg) ||
  196. g_lpConfig->ParseParams(argc - optind, &argv[optind]) < 0 ||
  197. (!bIgnoreUnknownConfigOptions && g_lpConfig->HasErrors())) {
  198. g_lpLogger = new ECLogger_File(1, 0, "-", false);
  199. if (g_lpLogger == nullptr) {
  200. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  201. goto exit;
  202. }
  203. ec_log_set(g_lpLogger);
  204. LogConfigErrors(g_lpConfig);
  205. goto exit;
  206. }
  207. g_lpLogger = CreateLogger(g_lpConfig, argv[0], "KopanoICal");
  208. if (!g_lpLogger) {
  209. fprintf(stderr, "Error loading configuration or parsing commandline arguments.\n");
  210. goto exit;
  211. }
  212. ec_log_set(g_lpLogger);
  213. if ((bIgnoreUnknownConfigOptions && g_lpConfig->HasErrors()) || g_lpConfig->HasWarnings())
  214. LogConfigErrors(g_lpConfig);
  215. if (!TmpPath::getInstance() -> OverridePath(g_lpConfig))
  216. ec_log_err("Ignoring invalid path-setting!");
  217. if (strncmp(g_lpConfig->GetSetting("process_model"), "thread", strlen("thread")) == 0)
  218. g_bThreads = true;
  219. // initialize SSL threading
  220. ssl_threading_setup();
  221. hr = HrSetupListeners(&ulListenCalDAV, &ulListenCalDAVs);
  222. if (hr != hrSuccess)
  223. goto exit;
  224. // setup signals
  225. signal(SIGTERM, sigterm);
  226. signal(SIGINT, sigterm);
  227. signal(SIGHUP, sighup);
  228. signal(SIGCHLD, sigchld);
  229. signal(SIGPIPE, SIG_IGN);
  230. memset(&st, 0, sizeof(st));
  231. memset(&act, 0, sizeof(act));
  232. st.ss_sp = malloc(65536);
  233. st.ss_flags = 0;
  234. st.ss_size = 65536;
  235. act.sa_sigaction = sigsegv;
  236. act.sa_flags = SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
  237. sigemptyset(&act.sa_mask);
  238. sigaltstack(&st, NULL);
  239. sigaction(SIGSEGV, &act, NULL);
  240. sigaction(SIGBUS, &act, NULL);
  241. sigaction(SIGABRT, &act, NULL);
  242. // fork if needed and drop privileges as requested.
  243. // this must be done before we do anything with pthreads
  244. if (unix_runas(g_lpConfig))
  245. goto exit;
  246. if (g_bDaemonize && unix_daemonize(g_lpConfig))
  247. goto exit;
  248. if (!g_bDaemonize)
  249. setsid();
  250. unix_create_pidfile(argv[0], g_lpConfig);
  251. if (g_bThreads == false)
  252. g_lpLogger = StartLoggerProcess(g_lpConfig, g_lpLogger);
  253. else
  254. g_lpLogger->SetLogprefix(LP_TID);
  255. ec_log_set(g_lpLogger);
  256. hr = MAPIInitialize(NULL);
  257. if (hr != hrSuccess) {
  258. fprintf(stderr, "Messaging API could not be initialized: %s (%x)",
  259. GetMAPIErrorMessage(hr), hr);
  260. goto exit;
  261. }
  262. if (g_bThreads)
  263. mainthread = pthread_self();
  264. ec_log_info("Starting kopano-ical version " PROJECT_VERSION_CALDAV_STR " (" PROJECT_SVN_REV_STR "), pid %d", getpid());
  265. hr = HrProcessConnections(ulListenCalDAV, ulListenCalDAVs);
  266. if (hr != hrSuccess)
  267. goto exit2;
  268. ec_log_info("CalDAV Gateway will now exit");
  269. // in forked mode, send all children the exit signal
  270. if (g_bThreads == false) {
  271. int i;
  272. signal(SIGTERM, SIG_IGN);
  273. kill(0, SIGTERM);
  274. i = 30; // wait max 30 seconds
  275. while (nChildren && i) {
  276. if (i % 5 == 0)
  277. ec_log_notice("Waiting for %d processes to exit", nChildren);
  278. sleep(1);
  279. --i;
  280. }
  281. if (nChildren)
  282. ec_log_notice("Forced shutdown with %d processes left", nChildren);
  283. else
  284. ec_log_info("CalDAV Gateway shutdown complete");
  285. }
  286. exit2:
  287. MAPIUninitialize();
  288. exit:
  289. free(st.ss_sp);
  290. ECChannel::HrFreeCtx();
  291. delete g_lpConfig;
  292. DeleteLogger(g_lpLogger);
  293. SSL_library_cleanup(); // Remove SSL data for the main application and other related libraries
  294. // Cleanup SSL parts
  295. ssl_threading_cleanup();
  296. // Cleanup libxml2 library
  297. xmlCleanupParser();
  298. // cleanup ICU data so valgrind is happy
  299. u_cleanup();
  300. return hr;
  301. }
  302. static HRESULT HrSetupListeners(int *lpulNormal, int *lpulSecure)
  303. {
  304. HRESULT hr;
  305. bool bListen;
  306. bool bListenSecure;
  307. int ulPortICal;
  308. int ulPortICalS;
  309. int ulNormalSocket = 0;
  310. int ulSecureSocket = 0;
  311. // setup sockets
  312. bListenSecure = (strcasecmp(g_lpConfig->GetSetting("icals_enable"), "yes") == 0);
  313. bListen = (strcasecmp(g_lpConfig->GetSetting("ical_enable"), "yes") == 0);
  314. if (!bListen && !bListenSecure) {
  315. ec_log_crit("No ports to open for listening.");
  316. return MAPI_E_INVALID_PARAMETER;
  317. }
  318. ulPortICal = atoi(g_lpConfig->GetSetting("ical_port"));
  319. ulPortICalS = atoi(g_lpConfig->GetSetting("icals_port"));
  320. // start listening on normal port
  321. if (bListen) {
  322. hr = HrListen(g_lpConfig->GetSetting("server_bind"), ulPortICal, &ulNormalSocket);
  323. if (hr != hrSuccess) {
  324. ec_log_crit("Could not listen on port %d. (0x%08X %s)", ulPortICal, hr, GetMAPIErrorMessage(hr));
  325. bListen = false;
  326. } else {
  327. ec_log_info("Listening on port %d.", ulPortICal);
  328. }
  329. }
  330. // start listening on secure port
  331. if (bListenSecure) {
  332. hr = ECChannel::HrSetCtx(g_lpConfig);
  333. if (hr == hrSuccess) {
  334. hr = HrListen(g_lpConfig->GetSetting("server_bind"), ulPortICalS, &ulSecureSocket);
  335. if (hr != hrSuccess) {
  336. ec_log_crit("Could not listen on secure port %d. (0x%08X %s)", ulPortICalS, hr, GetMAPIErrorMessage(hr));
  337. bListenSecure = false;
  338. }
  339. ec_log_info("Listening on secure port %d.", ulPortICalS);
  340. } else {
  341. ec_log_crit("Could not listen on secure port %d. (0x%08X %s)", ulPortICalS, hr, GetMAPIErrorMessage(hr));
  342. bListenSecure = false;
  343. }
  344. }
  345. if (!bListen && !bListenSecure) {
  346. ec_log_crit("No ports have been opened for listening, exiting.");
  347. return MAPI_E_INVALID_PARAMETER;
  348. }
  349. *lpulNormal = ulNormalSocket;
  350. *lpulSecure = ulSecureSocket;
  351. return hrSuccess;
  352. }
  353. /**
  354. * Listen to the passed sockets and calls HrStartHandlerClient for
  355. * every incoming connection.
  356. *
  357. * @param[in] ulNormalSocket Listening socket of incoming HTTP connections
  358. * @param[in] ulSecureSocket Listening socket of incoming HTTPS connections
  359. * @retval MAPI error code
  360. */
  361. static HRESULT HrProcessConnections(int ulNormalSocket, int ulSecureSocket)
  362. {
  363. HRESULT hr = hrSuccess;
  364. struct pollfd pollfd[2];
  365. bool bUseSSL;
  366. ECChannel *lpChannel = NULL;
  367. int nCloseFDs = 0, pCloseFDs[2] = {0}, pfd_normal = -1, pfd_secure = -1;
  368. if (ulNormalSocket) {
  369. pCloseFDs[nCloseFDs] = pollfd[nCloseFDs].fd = ulNormalSocket;
  370. pfd_normal = nCloseFDs++;
  371. }
  372. if (ulSecureSocket) {
  373. pCloseFDs[nCloseFDs] = pollfd[nCloseFDs].fd = ulSecureSocket;
  374. pfd_secure = nCloseFDs++;
  375. }
  376. for (size_t i = 0; i < nCloseFDs; ++i)
  377. pollfd[i].events = POLLIN | POLLRDHUP;
  378. // main program loop
  379. while (!g_bQuit) {
  380. for (size_t i = 0; i < nCloseFDs; ++i)
  381. pollfd[i].revents = 0;
  382. // Check whether there are incoming connections.
  383. int err = poll(pollfd, nCloseFDs, 10 * 1000);
  384. if (err < 0) {
  385. if (errno != EINTR) {
  386. ec_log_crit("An unknown socket error has occurred.");
  387. g_bQuit = true;
  388. hr = MAPI_E_NETWORK_ERROR;
  389. }
  390. continue;
  391. } else if (err == 0) {
  392. continue;
  393. }
  394. if (g_bQuit) {
  395. hr = hrSuccess;
  396. break;
  397. }
  398. // Check if a normal connection is waiting.
  399. if (pfd_normal >= 0 && pollfd[pfd_normal].revents & (POLLIN | POLLRDHUP)) {
  400. ec_log_info("Connection waiting on port %d.", atoi(g_lpConfig->GetSetting("ical_port")));
  401. bUseSSL = false;
  402. hr = HrAccept(ulNormalSocket, &lpChannel);
  403. if (hr != hrSuccess) {
  404. ec_log_err("Could not accept incoming connection on port %d. (0x%08X)", atoi(g_lpConfig->GetSetting("ical_port")), hr);
  405. continue;
  406. }
  407. // Check if a secure connection is waiting.
  408. } else if (pfd_secure >= 0 && pollfd[pfd_secure].revents & (POLLIN | POLLRDHUP)) {
  409. ec_log_info("Connection waiting on secure port %d.", atoi(g_lpConfig->GetSetting("icals_port")));
  410. bUseSSL = true;
  411. hr = HrAccept(ulSecureSocket, &lpChannel);
  412. if (hr != hrSuccess) {
  413. ec_log_err("Could not accept incoming secure connection on port %d. (0x%08X %s)", atoi(g_lpConfig->GetSetting("ical_port")), hr, GetMAPIErrorMessage(hr));
  414. continue;
  415. }
  416. } else {
  417. continue;
  418. }
  419. hr = HrStartHandlerClient(lpChannel, bUseSSL, nCloseFDs, pCloseFDs);
  420. if (hr != hrSuccess) {
  421. delete lpChannel; // destructor closes sockets
  422. ec_log_err("Handling client connection failed. (0x%08X %s)", hr, GetMAPIErrorMessage(hr));
  423. continue;
  424. }
  425. if (g_bThreads == false)
  426. delete lpChannel; // always cleanup channel in main process
  427. }
  428. return hr;
  429. }
  430. /**
  431. * Starts a new thread or forks a child to process the incoming
  432. * connection.
  433. *
  434. * @param[in] lpChannel The accepted connection in ECChannel object
  435. * @param[in] bUseSSL The ECChannel object is an SSL connection
  436. * @param[in] nCloseFDs Number of FDs in pCloseFDs, used on forks only
  437. * @param[in] pCloseFDs Array of FDs to close in child process
  438. * @retval E_FAIL when thread of child did not start
  439. */
  440. static HRESULT HrStartHandlerClient(ECChannel *lpChannel, bool bUseSSL,
  441. int nCloseFDs, int *pCloseFDs)
  442. {
  443. HRESULT hr = hrSuccess;
  444. pthread_attr_t pThreadAttr;
  445. pthread_t pThread;
  446. auto lpHandlerArgs = new HandlerArgs;
  447. lpHandlerArgs->lpChannel = lpChannel;
  448. lpHandlerArgs->bUseSSL = bUseSSL;
  449. if (g_bThreads) {
  450. pthread_attr_init(&pThreadAttr);
  451. if (pthread_attr_setdetachstate(&pThreadAttr, PTHREAD_CREATE_DETACHED) != 0)
  452. ec_log_warn("Could not set thread attribute to detached.");
  453. if (pthread_create(&pThread, &pThreadAttr, HandlerClient, lpHandlerArgs) != 0) {
  454. ec_log_err("Could not create thread.");
  455. hr = E_FAIL;
  456. goto exit;
  457. }
  458. set_thread_name(pThread, std::string("ZCalDAV") + lpChannel->peer_addr());
  459. }
  460. else {
  461. if (unix_fork_function(HandlerClient, lpHandlerArgs, nCloseFDs, pCloseFDs) < 0) {
  462. ec_log_err("Could not create process.");
  463. hr = E_FAIL;
  464. goto exit;
  465. }
  466. ++nChildren;
  467. }
  468. exit:
  469. if (hr != hrSuccess)
  470. delete lpHandlerArgs;
  471. return hr;
  472. }
  473. static void *HandlerClient(void *lpArg)
  474. {
  475. HRESULT hr = hrSuccess;
  476. auto lpHandlerArgs = static_cast<HandlerArgs *>(lpArg);
  477. ECChannel *lpChannel = lpHandlerArgs->lpChannel;
  478. bool bUseSSL = lpHandlerArgs->bUseSSL;
  479. delete lpHandlerArgs;
  480. if (bUseSSL && lpChannel->HrEnableTLS() != hrSuccess) {
  481. ec_log_err("Unable to negotiate SSL connection");
  482. goto exit;
  483. }
  484. while (!g_bQuit) {
  485. hr = lpChannel->HrSelect(KEEP_ALIVE_TIME);
  486. if (hr == MAPI_E_CANCEL)
  487. /* signalled - reevaluate g_bQuit */
  488. continue;
  489. if (hr != hrSuccess) {
  490. ec_log_info("Request timeout, closing connection");
  491. break;
  492. }
  493. //Save mapi session between Requests
  494. hr = HrHandleRequest(lpChannel);
  495. if (hr != hrSuccess)
  496. break;
  497. }
  498. exit:
  499. ec_log_info("Connection closed");
  500. delete lpChannel;
  501. return NULL;
  502. }
  503. static HRESULT HrHandleRequest(ECChannel *lpChannel)
  504. {
  505. HRESULT hr = hrSuccess;
  506. std::wstring wstrUser;
  507. std::wstring wstrPass;
  508. std::string strUrl;
  509. std::string strMethod;
  510. std::string strServerTZ = g_lpConfig->GetSetting("server_timezone");
  511. std::string strCharset;
  512. std::string strUserAgent, strUserAgentVersion;
  513. std::unique_ptr<ProtocolBase> lpBase;
  514. KCHL::object_ptr<IMAPISession> lpSession;
  515. Http lpRequest(lpChannel, g_lpConfig);
  516. ULONG ulFlag = 0;
  517. ec_log_debug("New Request");
  518. hr = lpRequest.HrReadHeaders();
  519. if(hr != hrSuccess) {
  520. hr = MAPI_E_USER_CANCEL; // connection is closed by client no data to be read
  521. goto exit;
  522. }
  523. hr = lpRequest.HrValidateReq();
  524. if(hr != hrSuccess) {
  525. lpRequest.HrResponseHeader(501, "Not Implemented");
  526. lpRequest.HrResponseBody("\nRequest not implemented");
  527. goto exit;
  528. }
  529. // ignore Empty Body
  530. lpRequest.HrReadBody();
  531. // no error, defaults to UTF-8
  532. lpRequest.HrGetCharSet(&strCharset);
  533. // ignore Empty User field.
  534. lpRequest.HrGetUser(&wstrUser);
  535. // ignore Empty Password field
  536. lpRequest.HrGetPass(&wstrPass);
  537. // no checks required as HrValidateReq() checks Method
  538. lpRequest.HrGetMethod(&strMethod);
  539. //
  540. lpRequest.HrSetKeepAlive(KEEP_ALIVE_TIME);
  541. lpRequest.HrGetUserAgent(&strUserAgent);
  542. lpRequest.HrGetUserAgentVersion(&strUserAgentVersion);
  543. hr = lpRequest.HrGetUrl(&strUrl);
  544. if (hr != hrSuccess) {
  545. ec_log_debug("URl is empty for method %s", strMethod.c_str());
  546. lpRequest.HrResponseHeader(400,"Bad Request");
  547. lpRequest.HrResponseBody("Bad Request");
  548. goto exit;
  549. }
  550. hr = HrParseURL(strUrl, &ulFlag);
  551. if (hr != hrSuccess) {
  552. ec_log_err("Client request is invalid: 0x%08X %s", hr, GetMAPIErrorMessage(hr));
  553. lpRequest.HrResponseHeader(400, "Bad Request: " + stringify(hr,true));
  554. goto exit;
  555. }
  556. if (ulFlag & SERVICE_CALDAV)
  557. // this header is always present in a caldav response, but not in ical.
  558. lpRequest.HrResponseHeader("DAV", "1, access-control, calendar-access, calendar-schedule, calendarserver-principal-property-search");
  559. if(!strMethod.compare("OPTIONS"))
  560. {
  561. lpRequest.HrResponseHeader(200, "OK");
  562. // @todo, if ical get is disabled, do not add GET as allowed option
  563. // @todo, should check write access on url and only return read actions if not available
  564. lpRequest.HrResponseHeader("Allow", "OPTIONS, GET, POST, PUT, DELETE, MOVE");
  565. lpRequest.HrResponseHeader("Allow", "PROPFIND, PROPPATCH, REPORT, MKCALENDAR");
  566. // most clients do not login with this action, no need to complain.
  567. hr = hrSuccess;
  568. goto exit;
  569. }
  570. if (wstrUser.empty() || wstrPass.empty()) {
  571. ec_log_info("Sending authentication request");
  572. hr = MAPI_E_CALL_FAILED;
  573. } else {
  574. lpRequest.HrGetMethod(&strMethod);
  575. hr = HrAuthenticate(strUserAgent, strUserAgentVersion, wstrUser, wstrPass, g_lpConfig->GetSetting("server_socket"), &~lpSession);
  576. if (hr != hrSuccess)
  577. ec_log_warn("Login failed (0x%08X %s), resending authentication request", hr, GetMAPIErrorMessage(hr));
  578. }
  579. if (hr != hrSuccess) {
  580. if(ulFlag & SERVICE_ICAL)
  581. lpRequest.HrRequestAuth("Kopano iCal Gateway");
  582. else
  583. lpRequest.HrRequestAuth("Kopano CalDav Gateway");
  584. hr = hrSuccess; //keep connection open.
  585. goto exit;
  586. }
  587. //GET & ical Requests
  588. // @todo fix caldav GET request
  589. static_assert(std::is_polymorphic<ProtocolBase>::value, "ProtocolBase needs to be polymorphic for unique_ptr to work");
  590. if( !strMethod.compare("GET") || !strMethod.compare("HEAD") || ((ulFlag & SERVICE_ICAL) && strMethod.compare("PROPFIND")) )
  591. {
  592. lpBase.reset(new iCal(&lpRequest, lpSession, strServerTZ, strCharset));
  593. }
  594. //CALDAV Requests
  595. else if((ulFlag & SERVICE_CALDAV) || ( !strMethod.compare("PROPFIND") && !(ulFlag & SERVICE_ICAL)))
  596. {
  597. lpBase.reset(new CalDAV(&lpRequest, lpSession, strServerTZ, strCharset));
  598. }
  599. else
  600. {
  601. hr = MAPI_E_CALL_FAILED;
  602. lpRequest.HrResponseHeader(404, "Request not valid for ical or caldav services");
  603. goto exit;
  604. }
  605. hr = lpBase->HrInitializeClass();
  606. if (hr != hrSuccess) {
  607. if (hr != MAPI_E_NOT_ME)
  608. hr = lpRequest.HrToHTTPCode(hr);
  609. goto exit;
  610. }
  611. hr = lpBase->HrHandleCommand(strMethod);
  612. exit:
  613. if(hr != hrSuccess && !strMethod.empty() && hr != MAPI_E_NOT_ME)
  614. ec_log_err("Error processing %s request, error code 0x%08x %s", strMethod.c_str(), hr, GetMAPIErrorMessage(hr));
  615. if (hr != MAPI_E_USER_CANCEL) // do not send response to client if connection closed by client.
  616. hr = lpRequest.HrFinalize();
  617. ec_log_debug("End Of Request");
  618. return hr;
  619. }