320 lines
9.3 KiB
C++
320 lines
9.3 KiB
C++
/*
|
|
* Copyright (C) 2009 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) version 3.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
#include <syncevo/CurlTransportAgent.h>
|
|
#include <syncevo/SuspendFlags.h>
|
|
|
|
#ifdef ENABLE_LIBCURL
|
|
|
|
#include <algorithm>
|
|
#include <ctime>
|
|
#include <syncevo/util.h>
|
|
|
|
#include <syncevo/declarations.h>
|
|
SE_BEGIN_CXX
|
|
|
|
|
|
CurlTransportAgent::CurlTransportAgent() :
|
|
m_easyHandle(easyInit()),
|
|
m_slist(NULL),
|
|
m_status(INACTIVE),
|
|
m_timeoutSeconds(0),
|
|
m_reply(NULL),
|
|
m_replyLen(0),
|
|
m_replySize(0)
|
|
{
|
|
#ifdef ENABLE_MAEMO /* hack because Maemo doesn't support IPv6 yet */
|
|
curl_easy_setopt(m_easyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
|
#endif
|
|
/*
|
|
* set up for post where message is pushed into curl via
|
|
* its read callback and reply is stored in write callback
|
|
*/
|
|
CURLcode code;
|
|
if ((code = curl_easy_setopt(m_easyHandle, CURLOPT_NOPROGRESS, false)) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_PROGRESSFUNCTION, progressCallback)) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_WRITEFUNCTION, writeDataCallback)) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_WRITEDATA, (void *)this)) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_READFUNCTION, readDataCallback)) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_READDATA, (void *)this)) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_ERRORBUFFER, this->m_curlErrorText )) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_AUTOREFERER, true)) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_POST, true)) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_FOLLOWLOCATION, true))) {
|
|
/* error encountered, throw exception */
|
|
curl_easy_cleanup(m_easyHandle);
|
|
checkCurl(code);
|
|
}
|
|
}
|
|
|
|
CURL *CurlTransportAgent::easyInit()
|
|
{
|
|
static bool initialized = false;
|
|
static CURLcode initres;
|
|
|
|
if (!initialized) {
|
|
initres = curl_global_init(CURL_GLOBAL_ALL);
|
|
initialized = true;
|
|
}
|
|
|
|
if (initres) {
|
|
SE_THROW_EXCEPTION(TransportException, "global curl initialization failed");
|
|
}
|
|
CURL *handle = curl_easy_init();
|
|
if (!handle) {
|
|
SE_THROW_EXCEPTION(TransportException, "no curl handle");
|
|
}
|
|
return handle;
|
|
}
|
|
|
|
CurlTransportAgent::~CurlTransportAgent()
|
|
{
|
|
if (m_reply) {
|
|
free(m_reply);
|
|
}
|
|
curl_easy_cleanup(m_easyHandle);
|
|
curl_slist_free_all(m_slist);
|
|
}
|
|
|
|
void CurlTransportAgent::setURL(const std::string &url)
|
|
{
|
|
m_url = url;
|
|
CURLcode code = curl_easy_setopt(m_easyHandle, CURLOPT_URL, m_url.c_str());
|
|
checkCurl(code);
|
|
}
|
|
|
|
void CurlTransportAgent::setProxy(const std::string &proxy)
|
|
{
|
|
m_proxy = proxy;
|
|
CURLcode code = curl_easy_setopt(m_easyHandle, CURLOPT_PROXY, m_proxy.c_str());
|
|
checkCurl(code);
|
|
}
|
|
|
|
void CurlTransportAgent::setProxyAuth(const std::string &user, const std::string &password)
|
|
{
|
|
m_auth = user + ":" + password;
|
|
CURLcode code = curl_easy_setopt(m_easyHandle, CURLOPT_PROXYUSERPWD,
|
|
m_auth.c_str());
|
|
checkCurl(code);
|
|
}
|
|
|
|
void CurlTransportAgent::setContentType(const std::string &type)
|
|
{
|
|
m_contentType = type;
|
|
}
|
|
|
|
void CurlTransportAgent::setUserAgent(const std::string &agent)
|
|
{
|
|
m_agent = agent;
|
|
CURLcode code = curl_easy_setopt(m_easyHandle, CURLOPT_USERAGENT,
|
|
m_agent.c_str());
|
|
checkCurl(code);
|
|
}
|
|
|
|
void CurlTransportAgent::setSSL(const std::string &cacerts,
|
|
bool verifyServer,
|
|
bool verifyHost)
|
|
{
|
|
m_cacerts = cacerts;
|
|
CURLcode code = CURLE_OK;
|
|
|
|
if (!m_cacerts.empty()) {
|
|
if (isDir(m_cacerts)) {
|
|
// libcurl + OpenSSL does not work with a directory set in CURLOPT_CAINFO.
|
|
// Must set the directory name as CURLOPT_CAPATH.
|
|
//
|
|
// Hopefully libcurl NSS also finds the directory name
|
|
// here ("NSS-powered libcurl provides the option only for
|
|
// backward compatibility. ").
|
|
code = curl_easy_setopt(m_easyHandle, CURLOPT_CAPATH, m_cacerts.c_str());
|
|
} else {
|
|
code = curl_easy_setopt(m_easyHandle, CURLOPT_CAINFO, m_cacerts.c_str());
|
|
}
|
|
}
|
|
if (!code) {
|
|
code = curl_easy_setopt(m_easyHandle, CURLOPT_SSL_VERIFYPEER, (long)verifyServer);
|
|
}
|
|
if (!code) {
|
|
code = curl_easy_setopt(m_easyHandle, CURLOPT_SSL_VERIFYHOST, (long)(verifyHost ? 2 : 0));
|
|
}
|
|
checkCurl(code);
|
|
}
|
|
|
|
void CurlTransportAgent::setTimeout(int seconds)
|
|
{
|
|
m_timeoutSeconds = seconds;
|
|
}
|
|
|
|
void CurlTransportAgent::shutdown()
|
|
{
|
|
}
|
|
|
|
void CurlTransportAgent::send(const char *data, size_t len)
|
|
{
|
|
CURLcode code;
|
|
|
|
m_replyLen = 0;
|
|
m_message = data;
|
|
m_messageSent = 0;
|
|
m_messageLen = len;
|
|
|
|
curl_slist_free_all(m_slist);
|
|
m_slist = NULL;
|
|
|
|
// Setting Expect explicitly prevents problems with certain
|
|
// proxies: if curl is allowed to depend on Expect, then it will
|
|
// send the POST header and wait for the servers reply that it is
|
|
// allowed to continue. This will always be the case with a correctly
|
|
// configured SyncML and because some proxies reject unknown Expect
|
|
// requests, it is better not used.
|
|
m_slist = curl_slist_append(m_slist, "Expect:");
|
|
|
|
std::string contentHeader("Content-Type: ");
|
|
contentHeader += m_contentType;
|
|
m_slist = curl_slist_append(m_slist, contentHeader.c_str());
|
|
|
|
m_status = ACTIVE;
|
|
if (m_timeoutSeconds) {
|
|
m_sendStartTime = Timespec::monotonic();
|
|
}
|
|
m_aborting = false;
|
|
if ((code = curl_easy_setopt(m_easyHandle, CURLOPT_PROGRESSDATA, static_cast<void *> (this)))||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_HTTPHEADER, m_slist)) ||
|
|
(code = curl_easy_setopt(m_easyHandle, CURLOPT_POSTFIELDSIZE, len))
|
|
){
|
|
m_status = CANCELED;
|
|
checkCurl(code);
|
|
}
|
|
|
|
if ((code = curl_easy_perform(m_easyHandle))) {
|
|
m_status = FAILED;
|
|
checkCurl(code, false);
|
|
} else {
|
|
m_status = GOT_REPLY;
|
|
}
|
|
}
|
|
|
|
void CurlTransportAgent::cancel()
|
|
{
|
|
/* nothing to do */
|
|
}
|
|
|
|
TransportAgent::Status CurlTransportAgent::wait(bool noReply)
|
|
{
|
|
return m_status;
|
|
}
|
|
|
|
void CurlTransportAgent::getReply(const char *&data, size_t &len, std::string &contentType)
|
|
{
|
|
data = m_reply;
|
|
len = m_replyLen;
|
|
const char *curlContentType;
|
|
if (!curl_easy_getinfo(m_easyHandle, CURLINFO_CONTENT_TYPE, &curlContentType) &&
|
|
curlContentType) {
|
|
contentType = curlContentType;
|
|
} else {
|
|
// unknown
|
|
contentType = "";
|
|
}
|
|
}
|
|
|
|
size_t CurlTransportAgent::writeDataCallback(void *buffer, size_t size, size_t nmemb, void *stream) throw()
|
|
{
|
|
return static_cast<CurlTransportAgent *>(stream)->writeData(buffer, size * nmemb);
|
|
}
|
|
|
|
size_t CurlTransportAgent::writeData(void *buffer, size_t size) throw()
|
|
{
|
|
bool increase = false;
|
|
while (m_replyLen + size > m_replySize) {
|
|
m_replySize = m_replySize ? m_replySize * 2 : 64 * 1024;
|
|
increase = true;
|
|
}
|
|
|
|
if (increase) {
|
|
m_reply = (char *)realloc(m_reply, m_replySize);
|
|
if (!m_reply) {
|
|
m_replySize = 0;
|
|
m_replyLen = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
memcpy(m_reply + m_replyLen,
|
|
buffer,
|
|
size);
|
|
m_replyLen += size;
|
|
return size;
|
|
}
|
|
|
|
size_t CurlTransportAgent::readDataCallback(void *buffer, size_t size, size_t nmemb, void *stream) throw()
|
|
{
|
|
return static_cast<CurlTransportAgent *>(stream)->readData(buffer, size * nmemb);
|
|
}
|
|
|
|
size_t CurlTransportAgent::readData(void *buffer, size_t size) throw()
|
|
{
|
|
size_t curr = std::min(size, m_messageLen - m_messageSent);
|
|
|
|
memcpy(buffer, m_message + m_messageSent, curr);
|
|
m_messageSent += curr;
|
|
return curr;
|
|
}
|
|
|
|
void CurlTransportAgent::checkCurl(CURLcode code, bool exception)
|
|
{
|
|
if (code) {
|
|
if(exception){
|
|
SE_THROW_EXCEPTION(TransportException, m_curlErrorText);
|
|
}else {
|
|
SE_LOG_INFO(NULL, NULL, "CurlTransport Failure: %s", m_curlErrorText);
|
|
}
|
|
}
|
|
}
|
|
|
|
int CurlTransportAgent::progressCallback(void* transport, double, double, double, double)
|
|
{
|
|
CurlTransportAgent *agent = static_cast<CurlTransportAgent *> (transport);
|
|
SuspendFlags &flags = SuspendFlags::getSuspendFlags();
|
|
// check signals and abort transfer?
|
|
flags.printSignals();
|
|
if (flags.getState() == SuspendFlags::ABORT) {
|
|
agent->setAborting (true);
|
|
return -1;
|
|
}
|
|
return agent->processCallback();
|
|
}
|
|
|
|
int CurlTransportAgent::processCallback()
|
|
{
|
|
if (m_timeoutSeconds) {
|
|
Timespec curTime = Timespec::monotonic();
|
|
if (curTime > m_sendStartTime + m_timeoutSeconds) {
|
|
m_status = TIME_OUT;
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
SE_END_CXX
|
|
|
|
#endif // ENABLE_LIBCURL
|
|
|