123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822 |
- /*
- * 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 <kopano/platform.h>
- #include <utility>
- #include "Http.h"
- #include <kopano/mapi_ptr.h>
- #include <kopano/stringutil.h>
- #include <kopano/ECConfig.h>
- using namespace std;
- /**
- * Parse the incoming URL into known pieces:
- *
- * /<service>/[username][/foldername][/uid.ics]
- * service: ical or caldav, mandatory
- * username: open store of this user, "public" for public folders. optional: default comes from HTTP Authenticate header.
- * foldername: folder name in store to open. multiple forms possible (normal name, prefix_guid, prefix_entryid).
- *
- * @param[in] strUrl incoming url
- * @param[out] lpulFlag url flags (service type and public marker)
- * @param[out] lpstrUrlUser owner of lpstrFolder
- * @param[out] lpstrFolder folder id or name
- *
- * @return
- */
- HRESULT HrParseURL(const std::string &strUrl, ULONG *lpulFlag, std::string *lpstrUrlUser, std::string *lpstrFolder)
- {
- HRESULT hr = hrSuccess;
- std::string strService;
- std::string strFolder;
- std::string strUrlUser;
- vector<std::string> vcUrlTokens;
- vector<std::string>::const_iterator iterToken;
- ULONG ulFlag = 0;
- vcUrlTokens = tokenize(strUrl, L'/', true);
- if (vcUrlTokens.empty())
- // root should be present, no flags are set. mostly used on OPTIONS command
- goto exit;
- if (vcUrlTokens.back().rfind(".ics") != string::npos)
- // Guid is retrieved using StripGuid().
- vcUrlTokens.pop_back();
- else
- // request is for folder not a calendar entry
- ulFlag |= REQ_COLLECTION;
- if (vcUrlTokens.empty())
- goto exit;
- if (vcUrlTokens.size() > 3) {
- // sub folders are not allowed
- hr = MAPI_E_TOO_COMPLEX;
- goto exit;
- }
- iterToken = vcUrlTokens.cbegin();
- //change case of Service name ICAL -> ical CALDaV ->caldav
- strService = strToLower(*iterToken++);
- if (!strService.compare("ical"))
- ulFlag |= SERVICE_ICAL;
- else if (!strService.compare("caldav"))
- ulFlag |= SERVICE_CALDAV;
- else
- ulFlag |= SERVICE_UNKNOWN;
- if (iterToken == vcUrlTokens.cend())
- goto exit;
- //change case of folder owner USER -> user, UseR -> user
- strUrlUser = strToLower(*iterToken++);
- // check if the request is for public folders and set the bool flag
- // @note: request for public folder not have user's name in the url
- if (!strUrlUser.compare("public"))
- ulFlag |= REQ_PUBLIC;
- if (iterToken == vcUrlTokens.cend())
- goto exit;
- // @todo subfolder/folder/ is not allowed! only subfolder/item.ics
- for (; iterToken != vcUrlTokens.end(); ++iterToken)
- strFolder = strFolder + *iterToken + "/";
- strFolder.erase(strFolder.length() - 1);
- exit:
- if (lpulFlag)
- *lpulFlag = ulFlag;
- if (lpstrUrlUser)
- lpstrUrlUser->swap(strUrlUser);
- if (lpstrFolder)
- lpstrFolder->swap(strFolder);
- return hr;
- }
- Http::Http(ECChannel *lpChannel, ECConfig *lpConfig)
- {
- m_lpChannel = lpChannel;
- m_lpConfig = lpConfig;
- m_ulKeepAlive = 0;
- m_ulRetCode = 0;
- }
- /**
- * Reads the http headers from the channel and Parses them
- *
- * @return HRESULT
- * @retval MAPI_E_INVALID_PARAMETER The http hearders are invalid
- */
- HRESULT Http::HrReadHeaders()
- {
- HRESULT hr;
- std::string strBuffer;
- ULONG n = 0;
- std::map<std::string, std::string>::iterator iHeader = mapHeaders.end();
- ec_log_debug("Receiving headers:");
- do
- {
- hr = m_lpChannel->HrReadLine(&strBuffer);
- if (hr != hrSuccess)
- return hr;
- if (strBuffer.empty())
- break;
- if (n == 0) {
- m_strAction = strBuffer;
- } else {
- std::string::size_type pos = strBuffer.find(':');
- std::string::size_type start = 0;
- if (strBuffer[0] == ' ' || strBuffer[0] == '\t') {
- if (iHeader == mapHeaders.end())
- continue;
- // continue header
- while (strBuffer[start] == ' ' || strBuffer[start] == '\t')
- ++start;
- iHeader->second += strBuffer.substr(start);
- } else {
- // new header
- auto r = mapHeaders.insert(make_pair<string,string>(strBuffer.substr(0,pos), strBuffer.substr(pos+2)));
- iHeader = r.first;
- }
- }
- if (strBuffer.find("Authorization") != string::npos)
- ec_log_debug("< Authorization: <value hidden>");
- else
- ec_log_debug("< "+strBuffer);
- ++n;
- } while(hr == hrSuccess);
- hr = HrParseHeaders();
- if (hr != hrSuccess)
- ec_log_debug("parsing headers failed: 0x%08X", hr);
- return hr;
- }
- /**
- * Parse the http headers
- * @return HRESULT
- * @retval MAPI_E_INVALID_PARAMETER The http headers are invalid
- */
- // @todo this does way too much.
- HRESULT Http::HrParseHeaders()
- {
- HRESULT hr;
- std::string strAuthdata;
- std::string strUserAgent;
- std::vector<std::string> items;
- std::string user_pass;
- size_t colon_pos;
- items = tokenize(m_strAction, ' ', true);
- if (items.size() != 3) {
- ec_log_debug("HrParseHeaders invalid != 3 tokens");
- return MAPI_E_INVALID_PARAMETER;
- }
- m_strMethod = items[0];
- m_strURL = items[1];
- m_strHttpVer = items[2];
- // converts %20 -> ' '
- m_strPath = urlDecode(m_strURL);
- // find the content-type
- // Content-Type: text/xml;charset=UTF-8
- hr = HrGetHeaderValue("Content-Type", &m_strCharSet);
- if (hr == hrSuccess && m_strCharSet.find("charset") != std::string::npos)
- m_strCharSet = m_strCharSet.substr(m_strCharSet.find("charset")+ strlen("charset") + 1, m_strCharSet.length());
- else
- m_strCharSet = m_lpConfig->GetSetting("default_charset"); // really should be UTF-8
- hr = HrGetHeaderValue("User-Agent", &strUserAgent);
- if (hr == hrSuccess) {
- size_t space = strUserAgent.find(" ");
- if (space != std::string::npos) {
- m_strUserAgent = strUserAgent.substr(0, space);
- m_strUserAgentVersion = strUserAgent.substr(space + 1);
- }
- else {
- m_strUserAgent = strUserAgent;
- }
- }
- // find the Authorisation data (Authorization: Basic wr8y273yr2y3r87y23ry7=)
- hr = HrGetHeaderValue("Authorization", &strAuthdata);
- if (hr != hrSuccess) {
- hr = HrGetHeaderValue("WWW-Authenticate", &strAuthdata);
- if (hr != hrSuccess)
- return S_OK; /* ignore empty Authorization */
- }
- items = tokenize(strAuthdata, ' ', true);
- // we only support basic authentication
- if (items.size() != 2 || items[0].compare("Basic") != 0) {
- ec_log_debug("HrParseHeaders login failed");
- return MAPI_E_LOGON_FAILED;
- }
- user_pass = base64_decode(items[1]);
- if((colon_pos = user_pass.find(":")) == std::string::npos) {
- ec_log_debug("HrParseHeaders password missing");
- return MAPI_E_LOGON_FAILED;
- }
- m_strUser = user_pass.substr(0, colon_pos);
- m_strPass = user_pass.substr(colon_pos+1, std::string::npos);
- return hrSuccess;
- }
- /**
- * Returns the user name set in the request
- * @param[in] strUser Return string for username in request
- * @return HRESULT
- * @retval MAPI_E_NOT_FOUND No username set in the request
- */
- HRESULT Http::HrGetUser(std::wstring *strUser)
- {
- if (m_strUser.empty())
- return MAPI_E_NOT_FOUND;
- return X2W(m_strUser, strUser);
- }
- /**
- * Returns the method set in the url
- * @param[out] strMethod Return string for method set in request
- * @return HRESULT
- * @retval MAPI_E_NOT_FOUND Empty method in request
- */
- HRESULT Http::HrGetMethod(std::string *strMethod)
- {
- if (m_strMethod.empty())
- return MAPI_E_NOT_FOUND;
- strMethod->assign(m_strMethod);
- return hrSuccess;
- }
- /**
- * Returns the password sent by user
- * @param[out] strPass The password is returned
- * @return HRESULT
- * @retval MAPI_E_NOT_FOUND Empty password in request
- */
- HRESULT Http::HrGetPass(std::wstring *strPass)
- {
- if (m_strPass.empty())
- return MAPI_E_NOT_FOUND;
- return X2W(m_strPass, strPass);
- }
- /**
- * return the original, non-decoded, url
- *
- * @param strURL us-ascii encoded url
- *
- * @return
- */
- HRESULT Http::HrGetRequestUrl(std::string *strURL)
- {
- strURL->assign(m_strURL);
- return hrSuccess;
- }
- /**
- * Returns the full decoded path of request(e.g. /caldav/user name/folder)
- * (eg. %20 is converted to ' ')
- * @param[out] strReqPath Return string for path
- * @return HRESULT
- * @retval MAPI_E_NOT_FOUND Empty path in request
- */
- HRESULT Http::HrGetUrl(std::string *strUrl)
- {
- if (m_strPath.empty())
- return MAPI_E_NOT_FOUND;
- strUrl->assign(urlDecode(m_strPath));
- return hrSuccess;
- }
- /**
- * Returns body of the request
- * @param[in] strBody Return string for body of the request
- * @return HRESULT
- * @retval MAPI_E_NOT_FOUND No body present in request
- */
- HRESULT Http::HrGetBody(std::string *strBody)
- {
- if (m_strReqBody.empty())
- return MAPI_E_NOT_FOUND;
- strBody->assign(m_strReqBody);
- return hrSuccess;
- }
- /**
- * Returns the Depth set in request
- * @param[out] ulDepth Return string for depth set in request
- * @return HRESULT
- * @retval MAPI_E_NOT_FOUND No depth value set in request
- */
- HRESULT Http::HrGetDepth(ULONG *ulDepth)
- {
- HRESULT hr = hrSuccess;
- std::string strDepth;
- /*
- * Valid input: [0, 1, infinity]
- */
- hr = HrGetHeaderValue("Depth", &strDepth);
- if (hr != hrSuccess)
- *ulDepth = 0; // default is no subfolders. default should become a parameter .. is action dependant
- else if (strDepth.compare("infinity") == 0)
- *ulDepth = 2;
- else {
- *ulDepth = atoi(strDepth.c_str());
- if (*ulDepth > 1)
- *ulDepth = 1;
- }
- return hr;
- }
- /**
- * Checks the etag of a MAPI object against If-(None)-Match headers
- *
- * @param[in] lpProp Object to check etag (PR_LAST_MODIFICATION_TIME) to
- *
- * @return continue request or return 412
- * @retval true continue request
- * @retval false return 412 to client
- */
- bool Http::CheckIfMatch(LPMAPIPROP lpProp)
- {
- bool ret = false;
- bool invert = false;
- string strIf;
- SPropValuePtr ptrLastModTime;
- string strValue;
- if (lpProp != nullptr &&
- HrGetOneProp(lpProp, PR_LAST_MODIFICATION_TIME, &~ptrLastModTime) == hrSuccess) {
- time_t stamp;
- FileTimeToUnixTime(ptrLastModTime->Value.ft, &stamp);
- strValue = stringify_int64(stamp, false);
- }
- if (HrGetHeaderValue("If-Match", &strIf) == hrSuccess) {
- if (strIf.compare("*") == 0 && !ptrLastModTime)
- // we have an object without a last mod time, not allowed
- return false;
- } else if (HrGetHeaderValue("If-None-Match", &strIf) == hrSuccess) {
- if (strIf.compare("*") == 0 && !!ptrLastModTime)
- // we have an object which has a last mod time, not allowed
- return false;
- invert = true;
- } else {
- return true;
- }
- // check all etags for a match
- for (auto &i : tokenize(strIf, ',', true)) {
- if (i.at(0) == '"' || i.at(0) == '\'')
- i.assign(i.begin() + 1, i.end() - 1);
- if (i.compare(strValue) == 0) {
- ret = true;
- break;
- }
- }
- if (invert)
- ret = !ret;
- return ret;
- }
- /**
- * Returns Charset of the request
- * @param[out] strCharset Return string Charset of the request
- *
- * @return HRESULT
- * @retval MAPI_E_NOT_FOUND No charset set in request
- */
- HRESULT Http::HrGetCharSet(std::string *strCharset)
- {
- if (m_strCharSet.empty())
- return MAPI_E_NOT_FOUND;
- strCharset->assign(m_strCharSet);
- return hrSuccess;
- }
- /**
- * Returns the Destination value found in the header
- *
- * Specifies the destination of entry in MOVE request,
- * to move mapi message from one folder to another
- * for eg.
- *
- * Destination: https://kopano.com:8080/caldav/USER/FOLDER-ID/ENTRY-GUID.ics
- *
- * @param[out] strDestination Return string destination of the request
- *
- * @return HRESULT
- * @retval MAPI_E_NOT_FOUND No destination set in request
- */
- HRESULT Http::HrGetDestination(std::string *strDestination)
- {
- HRESULT hr;
- std::string strHost;
- std::string strDest;
- string::size_type pos;
- // example: Host: server:port
- hr = HrGetHeaderValue("Host", &strHost);
- if(hr != hrSuccess) {
- ec_log_debug("Http::HrGetDestination host header missing");
- return hr;
- }
- // example: Destination: http://server:port/caldav/username/folderid/entry.ics
- hr = HrGetHeaderValue("Destination", &strDest);
- if (hr != hrSuccess) {
- ec_log_debug("Http::HrGetDestination destination header missing");
- return hr;
- }
- pos = strDest.find(strHost);
- if (pos == string::npos) {
- ec_log_err("Refusing to move calendar item from %s to different host on url %s", strHost.c_str(), strDest.c_str());
- return MAPI_E_CALL_FAILED;
- }
- strDest.erase(0, pos + strHost.length());
- *strDestination = std::move(strDest);
- return hrSuccess;
- }
- /**
- * Reads request body from the channel
- * @return HRESULT
- * @retval MAPI_E_NOT_FOUND Empty body
- */
- HRESULT Http::HrReadBody()
- {
- HRESULT hr = hrSuccess;
- int ulContLength;
- std::string strLength;
- // find the Content-Length
- if (HrGetHeaderValue("Content-Length", &strLength) != hrSuccess) {
- ec_log_debug("Http::HrReadBody content-length missing");
- return MAPI_E_NOT_FOUND;
- }
- ulContLength = atoi((char*)strLength.c_str());
- if (ulContLength <= 0) {
- ec_log_debug("Http::HrReadBody content-length invalid %d", ulContLength);
- return MAPI_E_NOT_FOUND;
- }
- hr = m_lpChannel->HrReadBytes(&m_strReqBody, ulContLength);
- if (!m_strUser.empty())
- ec_log_debug("Request body:\n%s\n", m_strReqBody.c_str());
- return hr;
- }
- /**
- * Check for errors in the http request
- * @return HRESULT
- * @retval MAPI_E_INVALID_PARAMETER Unsupported http request
- */
- HRESULT Http::HrValidateReq()
- {
- static const char *const lpszMethods[] = {
- "ACL", "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS",
- "PROPFIND", "REPORT", "MKCALENDAR", "PROPPATCH", "MOVE", NULL,
- };
- bool bFound = false;
- int i;
- if (m_strMethod.empty()) {
- static const HRESULT hr = MAPI_E_INVALID_PARAMETER;
- ec_log_err("HTTP request method is empty: %08X", hr);
- return hr;
- }
- if (!parseBool(m_lpConfig->GetSetting("enable_ical_get")) && m_strMethod == "GET") {
- ec_log_err("Denying iCalendar GET since it is disabled");
- return MAPI_E_NO_ACCESS;
- }
- for (i = 0; lpszMethods[i] != NULL; ++i) {
- if (m_strMethod.compare(lpszMethods[i]) == 0) {
- bFound = true;
- break;
- }
- }
- if (bFound == false) {
- static const HRESULT hr = MAPI_E_INVALID_PARAMETER;
- ec_log_err("HTTP request '%s' not implemented: %08X", m_strMethod.c_str(), hr);
- return hr;
- }
- // validate authentication data
- if (m_strUser.empty() || m_strPass.empty())
- // hr still success, since http request is valid
- ec_log_debug("Request missing authorization data");
- return hrSuccess;
- }
- /**
- * Sets keep-alive time for the http response
- *
- * @param[in] ulKeepAlive Numerical value set as keep-alive time of http connection
- * @return HRESULT Always set as hrSuccess
- */
- HRESULT Http::HrSetKeepAlive(int ulKeepAlive)
- {
- m_ulKeepAlive = ulKeepAlive;
- return hrSuccess;
- }
- /**
- * Flush all headers and body to client(i.e Send all data to client)
- *
- * Sends the data in chunked http if the data is large and client uses http 1.1
- * Example of Chunked http
- * 1A[CRLF] - size in hex
- * xxxxxxxxxxxx..[CRLF] - data
- * 1A[CRLF] - size in hex
- * xxxxxxxxxxxx..[CRLF] - data
- * 0[CRLF] - end of response
- * [CRLF]
- *
- * @return HRESULT
- * @retval MAPI_E_END_OF_SESSION States that client as set connection type as closed
- */
- HRESULT Http::HrFinalize()
- {
- HRESULT hr = hrSuccess;
- HrResponseHeader("Content-Length", stringify(m_strRespBody.length()));
- // force chunked http for long size response, should check version >= 1.1 to disable chunking
- if (m_strRespBody.size() < HTTP_CHUNK_SIZE || m_strHttpVer.compare("1.1") != 0)
- {
- hr = HrFlushHeaders();
- if (hr != hrSuccess && hr != MAPI_E_END_OF_SESSION) {
- ec_log_debug("Http::HrFinalize flush fail %d", hr);
- m_ulRetCode = 0;
- return hr;
- }
- if (!m_strRespBody.empty()) {
- m_lpChannel->HrWriteString(m_strRespBody);
- ec_log_debug("Response body:\n%s", m_strRespBody.c_str());
- }
- }
- else
- {
- const char *lpstrBody = m_strRespBody.data();
- char lpstrLen[10];
- std::string::size_type szBodyLen = m_strRespBody.size(); // length of data to be sent to the client
- std::string::size_type szBodyWritten = 0; // length of data sent to client
- unsigned int szPart = HTTP_CHUNK_SIZE; // default lenght of chunk data to be written
- HrResponseHeader("Transfer-Encoding", "chunked");
- hr = HrFlushHeaders();
- if (hr != hrSuccess && hr != MAPI_E_END_OF_SESSION) {
- ec_log_debug("Http::HrFinalize flush fail(2) %d", hr);
- m_ulRetCode = 0;
- return hr;
- }
- while (szBodyWritten < szBodyLen)
- {
- if ((szBodyWritten + HTTP_CHUNK_SIZE) > szBodyLen)
- szPart = szBodyLen - szBodyWritten; // change length of data for last chunk
- // send hex length of data and data part
- snprintf(lpstrLen, sizeof(lpstrLen), "%X", szPart);
- m_lpChannel->HrWriteLine(lpstrLen);
- m_lpChannel->HrWriteLine((char*)lpstrBody, szPart);
- szBodyWritten += szPart;
- lpstrBody += szPart;
- }
- // end of response
- snprintf(lpstrLen, 10, "0\r\n");
- m_lpChannel->HrWriteLine(lpstrLen);
- // just the first part of the body in the log. header shows it's chunked.
- ec_log_debug("%s", m_strRespBody.c_str());
- }
- // if http_log_enable?
- char szTime[32];
- time_t now = time(NULL);
- tm local;
- string strAgent;
- localtime_r(&now, &local);
- // @todo we're in C LC_TIME locale to get the correct (month) format, but the timezone will be GMT, which is not wanted.
- strftime(szTime, ARRAY_SIZE(szTime), "%d/%b/%Y:%H:%M:%S %z", &local);
- HrGetHeaderValue("User-Agent", &strAgent);
- ec_log_notice("%s - %s [%s] \"%s\" %d %d \"-\" \"%s\"", m_lpChannel->peer_addr(), m_strUser.empty() ? "-" : m_strUser.c_str(), szTime, m_strAction.c_str(), m_ulRetCode, (int)m_strRespBody.length(), strAgent.c_str());
- m_ulRetCode = 0;
- return hr;
- }
- /**
- * Converts common hr codes to http error codes
- *
- * @param hr HRESULT
- *
- * @return Error from HrResponseHeader(unsigned int, string)
- */
- HRESULT Http::HrToHTTPCode(HRESULT hr)
- {
- if (hr == hrSuccess)
- return HrResponseHeader(200, "Ok");
- else if (hr == MAPI_E_NO_ACCESS)
- return HrResponseHeader(403, "Forbidden");
- else if (hr == MAPI_E_NOT_FOUND)
- return HrResponseHeader(404, "Not Found");
- // @todo other codes?
- return HrResponseHeader(500, "Unhanded error " + stringify(hr, true));
- }
- /**
- * Sets http response headers
- * @param[in] ulCode Http header status code
- * @param[in] strResponse Http header status string
- *
- * @return HRESULT
- * @retval MAPI_E_CALL_FAILED The status is already set
- *
- */
- HRESULT Http::HrResponseHeader(unsigned int ulCode, std::string strResponse)
- {
- m_ulRetCode = ulCode;
- // do not set headers if once set
- if (!m_strRespHeader.empty())
- return MAPI_E_CALL_FAILED;
- m_strRespHeader = "HTTP/1.1 " + stringify(ulCode) + " " + strResponse;
- return hrSuccess;
- }
- /**
- * Adds response header to the list of headers
- * @param[in] strHeader Name of the header eg. Connection, Host, Date
- * @param[in] strValue Value of the header to be set
- * @return HRESULT Always set to hrSuccess
- */
- HRESULT Http::HrResponseHeader(std::string strHeader, std::string strValue)
- {
- m_lstHeaders.push_back(strHeader + ": " + strValue);
- return hrSuccess;
- }
- /**
- * Add string to http response body
- * @param[in] strResponse The string to be added to http response body
- * return HRESULT Always set to hrSuccess
- */
- HRESULT Http::HrResponseBody(std::string strResponse)
- {
- m_strRespBody += strResponse;
- // data send in HrFinalize()
- return hrSuccess;
- }
- /**
- * Request authorization information from the client
- *
- * @param[in] strMsg Message to be shown to the client
- * @return HRESULT
- */
- HRESULT Http::HrRequestAuth(std::string strMsg)
- {
- HRESULT hr = HrResponseHeader(401, "Unauthorized");
- if (hr != hrSuccess)
- return hr;
- strMsg = "Basic realm=\"" + strMsg + "\"";
- hr = HrResponseHeader("WWW-Authenticate", strMsg);
- if (hr != hrSuccess)
- return hr;
- return hrSuccess;
- }
- /**
- * Write all http headers to ECChannel
- * @retrun HRESULT
- * @retval MAPI_E_END_OF_SESSION If Connection type is set to close then the mapi session is ended
- */
- HRESULT Http::HrFlushHeaders()
- {
- HRESULT hr = hrSuccess;
- std::string strOutput;
- char lpszChar[128];
- time_t tmCurrenttime = time(NULL);
- std::string strConnection;
- HrGetHeaderValue("Connection", &strConnection);
- // Add misc. headers
- HrResponseHeader("Server","Kopano");
- struct tm dummy;
- strftime(lpszChar, 127, "%a, %d %b %Y %H:%M:%S GMT", gmtime_safe(&tmCurrenttime, &dummy));
- HrResponseHeader("Date", lpszChar);
- if (m_ulKeepAlive != 0 && strcasecmp(strConnection.c_str(), "keep-alive") == 0) {
- HrResponseHeader("Connection", "Keep-Alive");
- HrResponseHeader("Keep-Alive", stringify(m_ulKeepAlive, false));
- }
- else
- {
- HrResponseHeader("Connection", "close");
- hr = MAPI_E_END_OF_SESSION;
- }
- // create headers packet
- assert(m_ulRetCode != 0);
- if (m_ulRetCode == 0)
- HrResponseHeader(500, "Request handled incorrectly");
- ec_log_debug("> " + m_strRespHeader);
- strOutput += m_strRespHeader + "\r\n";
- m_strRespHeader.clear();
- for (const auto &h : m_lstHeaders) {
- ec_log_debug("> " + h);
- strOutput += h + "\r\n";
- }
- m_lstHeaders.clear();
- //as last line has a CRLF. The HrWriteLine adds one more CRLF.
- //this means the End of headder.
- m_lpChannel->HrWriteLine(strOutput);
- return hr;
- }
- HRESULT Http::X2W(const std::string &strIn, std::wstring *lpstrOut)
- {
- const char *lpszCharset = (m_strCharSet.empty() ? "UTF-8" : m_strCharSet.c_str());
- return TryConvert(m_converter, strIn, rawsize(strIn), lpszCharset, *lpstrOut);
- }
- HRESULT Http::HrGetHeaderValue(const std::string &strHeader, std::string *strValue)
- {
- auto iHeader = mapHeaders.find(strHeader);
- if (iHeader == mapHeaders.cend())
- return MAPI_E_NOT_FOUND;
- *strValue = iHeader->second;
- return hrSuccess;
- }
- HRESULT Http::HrGetUserAgent(std::string *strUserAgent)
- {
- if (m_strUserAgent.empty())
- return MAPI_E_NOT_FOUND;
- strUserAgent -> assign(m_strUserAgent);
- return hrSuccess;
- }
- HRESULT Http::HrGetUserAgentVersion(std::string *strUserAgentVersion)
- {
- if (m_strUserAgentVersion.empty())
- return MAPI_E_NOT_FOUND;
- strUserAgentVersion -> assign(m_strUserAgentVersion);
- return hrSuccess;
- }
|