summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HACKING6
-rw-r--r--configure-post.in1
-rw-r--r--configure-pre.in97
-rw-r--r--src/core/CurlTransportAgent.cpp216
-rw-r--r--src/core/CurlTransportAgent.h98
-rw-r--r--src/core/EvolutionSmartPtr.h2
-rw-r--r--src/core/EvolutionSyncClient.cpp78
-rw-r--r--src/core/EvolutionSyncClient.h25
-rw-r--r--src/core/Makefile.am11
-rw-r--r--src/core/SoupTransportAgent.cpp142
-rw-r--r--src/core/SoupTransportAgent.h85
-rw-r--r--src/core/TransportAgent.cpp23
-rw-r--r--src/core/TransportAgent.h135
13 files changed, 853 insertions, 66 deletions
diff --git a/HACKING b/HACKING
index fae9f174..c4062b1b 100644
--- a/HACKING
+++ b/HACKING
@@ -59,7 +59,11 @@ configure options for details.
For doing development work the recommended configure line is:
configure SYNCEVOLUTION_CXXFLAGS="-Wall -Werror -Wno-unknown-pragmas" \
- --enable-unit-tests
+ --enable-unit-tests \
+ --enable-libcurl
+
+Enabling libcurl explicitly ensures that it gets built even when not
+the default.
In contrast to CXXFLAGS, SYNCEVOLUTION_CXXFLAGS adds these flags only
to the compilation of source files from the SyncEvolution source code
diff --git a/configure-post.in b/configure-post.in
index 3fb0bd94..10f8013c 100644
--- a/configure-post.in
+++ b/configure-post.in
@@ -88,4 +88,3 @@ AC_DEFINE(SYNTHESIS, 1, "using Synthesis engine")
AC_CONFIG_FILES(Makefile src/Makefile src/core/Makefile test/Makefile)
AC_OUTPUT
-
diff --git a/configure-pre.in b/configure-pre.in
index 066b2f38..8fd96e02 100644
--- a/configure-pre.in
+++ b/configure-pre.in
@@ -140,39 +140,82 @@ FUNAMBOL_LIB=$PWD/src/build-client-api/src/libfunambol.la
AC_SUBST(CLEAN_CLIENT_SRC)
-case $target in
-*apple-darwin*) use_mac_ta="yes" ;;
-*) use_libcurl="yes" ;;
-esac
-
-# Check for libcurl now instead of during make.
-# Strictly speaking, building libfunambol takes
-# care of that, but because we build it in a
-# slightly unusual way (build libfunambol.a with
-# -fPIC, include just the required object files
-# in libsyncevolution) libtool doesn't pick up
-# that dependency and we have to add libcurl to
-# out link flags ourselves.
+# Check for transport layer.
+# Both curl and libsoup can be enabled and disabled explicitly.
+# The default is to use libsoup if available, otherwise curl.
+
AC_MSG_CHECKING([for libcurl])
-if TRANSPORT_LDFLAGS=`sh -c 'curl-config --libs' 2>&AS_MESSAGE_LOG_FD` && \
- TRANSPORT_CFLAGS=`sh -c 'curl-config --cflags' 2>&AS_MESSAGE_LOG_FD`; then
- AC_MSG_RESULT([found])
+if LIBCURL_LIBS=`sh -c 'curl-config --libs' 2>&AS_MESSAGE_LOG_FD` && \
+ LIBCURL_CFLAGS=`sh -c 'curl-config --cflags' 2>&AS_MESSAGE_LOG_FD`; then
+ AC_MSG_RESULT([yes])
+ have_libcurl="yes"
else
- AC_MSG_RESULT([missing])
- # FIXME: hard-coded check for Mac (same as in libfunambol)
- case $target in
- *apple-darwin*) TRANSPORT_LDFLAGS="-framework CoreServices" ;;
- *) AC_MSG_ERROR([libcurl is required, check that its development package is installed and curl-config is in your PATH]) ;;
- esac
+ AC_MSG_RESULT([no])
+ have_libcurl="no"
+fi
+PKG_CHECK_MODULES(LIBSOUP, libsoup-2.4,
+ have_libsoup="yes",
+ have_libsoup="no")
+
+TRANSPORT=
+TRANSPORT_LIBS=
+TRANSPORT_CFLAGS=
+
+# choose default transport (mirrors code in EvolutionSyncClient::createTransportAgent())
+if test "$have_libsoup" = "yes"; then
+ default_transport="libsoup"
+elif test "$have_libcurl" = "yes"; then
+ default_transport="libcurl"
fi
-AC_SUBST(TRANSPORT_LDFLAGS)
-AC_SUBST(TRANSPORT_CFLAGS)
-if test "x$use_mac_ta" = "xyes"; then
- CURL_LDFLAGS="-framework CoreServices"
- AC_SUBST(CURL_LDFLAGS)
+AC_ARG_ENABLE(libcurl,
+ AC_HELP_STRING([--enable-libcurl],
+ [enable libcurl as transport layer]),
+ [ if test "$enableval" = "yes"; then
+ test "$have_libcurl" = "yes" || AC_MSG_ERROR([libcurl not found])
+ TRANSPORT="$TRANSPORT libcurl"
+ TRANSPORT_LIBS="$TRANSPORT_LIBS $LIBCURL_LIBS"
+ TRANSPORT_CFLAGS="$TRANSPORT_CFLAGS $LIBCURL_CFLAGS"
+ AC_DEFINE(ENABLE_LIBCURL, 1, [enable libcurl transport])
+ else
+ libcurl_disabled="yes"
+ fi ],
+ [ if test "$have_libcurl" = "yes" && test "$default_transport" = "libcurl" ; then
+ TRANSPORT="$TRANSPORT libcurl"
+ TRANSPORT_LIBS="$TRANSPORT_LIBS $LIBCURL_LIBS"
+ TRANSPORT_CFLAGS="$TRANSPORT_CFLAGS $LIBCURL_CFLAGS"
+ AC_DEFINE(ENABLE_LIBCURL, 1, [enable libcurl transport])
+ fi ])
+
+AC_ARG_ENABLE(libsoup,
+ AC_HELP_STRING([--enable-libsoup],
+ [enable libsoup as transport layer]),
+ [ if test "$enableval" = "yes"; then
+ test "$have_libsoup" = "yes" || AC_MSG_ERROR([libsoup not found])
+ TRANSPORT="$TRANSPORT libsoup"
+ TRANSPORT_LIBS="$TRANSPORT_LIBS $LIBSOUP_LIBS"
+ TRANSPORT_CFLAGS="$TRANSPORT_CFLAGS $LIBSOUP_CFLAGS"
+ AC_DEFINE(ENABLE_LIBSOUP, 1, [enable libsoup transport])
+ else
+ libsoup_disabled="yes"
+ fi ],
+ [ if test "$have_libsoup" = "yes" && test "$default_transport" = "libsoup"; then
+ TRANSPORT="$TRANSPORT libsoup"
+ TRANSPORT_LIBS="$TRANSPORT_LIBS $LIBSOUP_LIBS"
+ TRANSPORT_CFLAGS="$TRANSPORT_CFLAGS $LIBSOUP_CFLAGS"
+ AC_DEFINE(ENABLE_LIBSOUP, 1, [enable libsoup transport])
+ fi ])
+
+if test ! "$TRANSPORT" &&
+ test "$libsoup_disabled" != "yes" &&
+ test "$libcurl_disabled" != "yes"; then
+ AC_ERROR([no transport library found, configure with --disable-libcurl --disable-libsoup to continue anyway (only useful if users of libsyncevolution provide transport implementation)])
fi
+AC_SUBST(TRANSPORT_LIBS)
+AC_SUBST(TRANSPORT_CFLAGS)
+
+
# absolute patch to source of Funambol client library
CLIENT_API_SRC=no-client-api-source
AC_SUBST(CLIENT_API_SRC)
diff --git a/src/core/CurlTransportAgent.cpp b/src/core/CurlTransportAgent.cpp
new file mode 100644
index 00000000..4b315dfa
--- /dev/null
+++ b/src/core/CurlTransportAgent.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2009 Patrick Ohly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "CurlTransportAgent.h"
+
+#ifdef ENABLE_LIBCURL
+
+#include <algorithm>
+
+namespace SyncEvolution {
+
+CurlTransportAgent::CurlTransportAgent() :
+ m_easyHandle(easyInit()),
+ m_status(INACTIVE),
+ m_reply(NULL),
+ m_replyLen(0),
+ m_replySize(0)
+{
+ /*
+ * 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, true)) ||
+ (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) {
+ throw std::string("global curl initialization failed");
+ }
+ CURL *handle = curl_easy_init();
+ if (!handle) {
+ throw "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)
+{
+ CURLcode code = curl_easy_setopt(m_easyHandle, CURLOPT_URL, url.c_str());
+ checkCurl(code);
+}
+
+void CurlTransportAgent::setProxy(const std::string &proxy)
+{
+ CURLcode code = curl_easy_setopt(m_easyHandle, CURLOPT_PROXY, proxy.c_str());
+ checkCurl(code);
+}
+
+void CurlTransportAgent::setProxyAuth(const std::string &user, const std::string &password)
+{
+ CURLcode code = curl_easy_setopt(m_easyHandle, CURLOPT_PROXYUSERPWD,
+ (user + ":" + password).c_str());
+ checkCurl(code);
+}
+
+void CurlTransportAgent::setContentType(const std::string &type)
+{
+ m_contentType = type;
+}
+
+/**
+ * @TODO: curl_easy_setopt(m_easyHandle, CURLOPT_CAINFO, certificates
+ * (code = curl_easy_setopt(m_easyHandle, CURLOPT_SSL_VERIFYPEER, (long)SSLVerifyServer)) ||
+ * (code = curl_easy_setopt(m_easyHandle, CURLOPT_SSL_VERIFYHOST, (long)(SSLVerifyHost ? 2 : 0))) ||
+ */
+
+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 ((code = curl_easy_setopt(m_easyHandle, CURLOPT_HTTPHEADER, m_slist)) ||
+ (code = curl_easy_setopt(m_easyHandle, CURLOPT_POSTFIELDSIZE, len)) ||
+ (code = curl_easy_perform(m_easyHandle))) {
+ m_status = CANCELED;
+ checkCurl(code);
+ }
+ m_status = GOT_REPLY;
+}
+
+void CurlTransportAgent::cancel()
+{
+ /* nothing to do */
+}
+
+TransportAgent::Status CurlTransportAgent::wait()
+{
+ return m_status;
+}
+
+void CurlTransportAgent::getReply(const char *&data, size_t &len)
+{
+ data = m_reply;
+ len = m_replyLen;
+}
+
+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)
+{
+ if (code) {
+ /** @TODO: logging */
+ throw std::string(m_curlErrorText);
+ }
+}
+
+} // namespace SyncEvolution
+
+#endif // ENABLE_LIBCURL
diff --git a/src/core/CurlTransportAgent.h b/src/core/CurlTransportAgent.h
new file mode 100644
index 00000000..f45ed170
--- /dev/null
+++ b/src/core/CurlTransportAgent.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009 Patrick Ohly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef INCL_CURLTRANSPORTAGENT
+#define INCL_CURLTRANSPORTAGENT
+
+#include <config.h>
+
+#ifdef ENABLE_LIBCURL
+
+#include "TransportAgent.h"
+#include <curl/curl.h>
+
+namespace SyncEvolution {
+
+/**
+ * message send/receive with curl
+ *
+ * The simple curl API is used, so sending blocks until the
+ * reply is ready.
+ */
+class CurlTransportAgent : public TransportAgent
+{
+ public:
+ CurlTransportAgent();
+ ~CurlTransportAgent();
+
+ virtual void setURL(const std::string &url);
+ virtual void setProxy(const std::string &proxy);
+ virtual void setProxyAuth(const std::string &user, const std::string &password);
+ virtual void setContentType(const std::string &type);
+ virtual void send(const char *data, size_t len);
+ virtual void cancel();
+ virtual Status wait();
+ virtual void getReply(const char *&data, size_t &len);
+
+ private:
+ CURL *m_easyHandle;
+ curl_slist *m_slist;
+ std::string m_contentType;
+ Status m_status;
+
+ /** message buffer (owned by caller) */
+ const char *m_message;
+ /** number of valid bytes in m_message */
+ size_t m_messageLen;
+ /** number of sent bytes in m_message */
+ size_t m_messageSent;
+
+ /** reply buffer */
+ char *m_reply;
+ /** number of valid bytes in m_reply */
+ size_t m_replyLen;
+ /** total buffer size */
+ size_t m_replySize;
+
+ /** error text from curl, set via CURLOPT_ERRORBUFFER */
+ char m_curlErrorText[CURL_ERROR_SIZE];
+
+ /** CURLOPT_READFUNCTION, stream == CurlTransportAgent */
+ static size_t readDataCallback(void *buffer, size_t size, size_t nmemb, void *stream) throw();
+ size_t readData(void *buffer, size_t size) throw();
+
+ /** CURLOPT_WRITEFUNCTION, stream == CurlTransportAgent */
+ static size_t writeDataCallback(void *ptr, size_t size, size_t nmemb, void *stream) throw();
+ size_t writeData(void *buffer, size_t size) throw();
+
+ /** check curl error code and turn into exception */
+ void checkCurl(CURLcode code);
+
+ /**
+ * initialize curl if necessary, return new handle
+ *
+ * Never returns NULL, instead throws exceptions.
+ */
+ static CURL *easyInit();
+};
+
+} // namespace SyncEvolution
+
+
+#endif // ENABLE_LIBCURL
+#endif // INCL_TRANSPORTAGENT
diff --git a/src/core/EvolutionSmartPtr.h b/src/core/EvolutionSmartPtr.h
index 80d39d9b..1e67a87d 100644
--- a/src/core/EvolutionSmartPtr.h
+++ b/src/core/EvolutionSmartPtr.h
@@ -34,7 +34,7 @@ class EvolutionUnref {
*/
static void unref(char *pointer) { free(pointer); }
-#ifdef HAVE_EDS
+#ifdef HAVE_GLIB
static void unref(GObject *pointer) { g_object_unref(pointer); }
/** free a list of GObject and the objects */
static void unref(GList *pointer) {
diff --git a/src/core/EvolutionSyncClient.cpp b/src/core/EvolutionSyncClient.cpp
index eb091cab..37896d08 100644
--- a/src/core/EvolutionSyncClient.cpp
+++ b/src/core/EvolutionSyncClient.cpp
@@ -7,7 +7,9 @@
#include "SyncEvolutionUtil.h"
#include <posix/base/posixlog.h>
-#include <posix/http/CurlTransportAgent.h>
+#include "TransportAgent.h"
+#include "CurlTransportAgent.h"
+#include "SoupTransportAgent.h"
#include <list>
#include <memory>
@@ -658,6 +660,19 @@ string EvolutionSyncClient::askPassword(const string &descr)
}
}
+boost::shared_ptr<TransportAgent> EvolutionSyncClient::createTransportAgent()
+{
+#ifdef ENABLE_LIBSOUP
+ boost::shared_ptr<SoupTransportAgent> agent(new SoupTransportAgent());
+#elif defined(ENABLE_LIBCURL)
+ boost::shared_ptr<CurlTransportAgent> agent(new CurlTransportAgent());
+#else
+ boost::shared_ptr<TransportAgent> agent;
+ throw std::string("libsyncevolution was compiled without default transport, client must implement EvolutionSyncClient::createTransportAgent()");
+#endif
+ return agent;
+}
+
void EvolutionSyncClient::displayServerMessage(const string &message)
{
LOG.info("message from server: %s",
@@ -1292,17 +1307,13 @@ void EvolutionSyncClient::doSync()
}
// run an HTTP client sync session
- Proxy proxy;
+ boost::shared_ptr<TransportAgent> agent(createTransportAgent());
if (getUseProxy()) {
- proxy.setProxy(getProxyHost(),
- getProxyPort(),
- getProxyUsername(),
- getProxyPassword());
+ agent->setProxy(getProxyHost());
+ agent->setProxyAuth(getProxyUsername(),
+ getProxyPassword());
}
- URL url;
- CurlTransportAgent agent(url, proxy,
- getResponseTimeout());
- agent.setUserAgent(getUserAgent());
+ // TODO: agent->setUserAgent(getUserAgent());
// TODO: SSL settings
sysync::SessionH sessionH;
@@ -1316,7 +1327,7 @@ void EvolutionSyncClient::doSync()
// Sync main loop: runs until SessionStep() signals end or error.
// Exceptions are caught and lead to a call of SessionStep() with
// parameter STEPCMD_ABORT -> abort session as soon as possible.
- char *received = NULL;
+ sysync::memSize length = 0;
do {
try {
// take next step
@@ -1407,52 +1418,51 @@ void EvolutionSyncClient::doSync()
getEngine().GetStrValue(sessionKeyH,
"connectURI",
s);
- URL newurl(s.c_str());
- agent.setURL(newurl);
+ agent->setURL(s);
string contenttype;
getEngine().GetStrValue(sessionKeyH,
"contenttype",
contenttype);
getEngine().CloseKey(sessionKeyH);
+ agent->setContentType(contenttype);
// use GetSyncMLBuffer()/RetSyncMLBuffer() to access the data to be
// sent or have it copied into caller's buffer using
// ReadSyncMLBuffer(), then send it to the server
sysync::appPointer buffer;
- sysync::memSize length;
err = getEngine().GetSyncMLBuffer(sessionH, true, buffer, length);
if (err) {
throwError("buffer");
}
- // need temporary null-terminated string for agent
- string tmp;
- tmp.append((const char *)buffer, 0, length);
- received = agent.sendMessage(tmp.c_str());
- getEngine().RetSyncMLBuffer(sessionH, true, length);
-
- // status for next step
- if (received) {
- stepCmd = STEPCMD_SENTDATA; // we have sent the data and received response
- } else {
- stepCmd = STEPCMD_TRANSPFAIL; // communication with server failed
- }
+ agent->send(static_cast<const char *>(buffer), length);
+ stepCmd = STEPCMD_SENTDATA; // we have sent the data
break;
}
case STEPCMD_NEEDDATA:
- // put answer received earlier into SyncML engine's buffer
- if (received) {
- err = getEngine().WriteSyncMLBuffer(sessionH, received, strlen(received));
+ switch (agent->wait()) {
+ case TransportAgent::ACTIVE:
+ stepCmd = STEPCMD_SENTDATA; // still sending the data?!
+ break;
+ case TransportAgent::GOT_REPLY:
+ getEngine().RetSyncMLBuffer(sessionH, true, length);
+ const char *reply;
+ size_t replylen;
+ agent->getReply(reply, replylen);
+
+ // put answer received earlier into SyncML engine's buffer
+ err = getEngine().WriteSyncMLBuffer(sessionH,
+ const_cast<void *>(static_cast<const void *>(reply)),
+ replylen);
if (err) {
throwError("write buffer");
}
- delete [] received;
- received = NULL;
stepCmd = STEPCMD_GOTDATA; // we have received response data
- } else {
+ break;
+ default:
stepCmd = STEPCMD_TRANSPFAIL; // communication with server failed
+ break;
}
- break;
- } // switch stepcmd
+ }
}
// check for suspend or abort, if so, modify step command for next step
if (false /* tdb: check if user requests suspending the session */) {
diff --git a/src/core/EvolutionSyncClient.h b/src/core/EvolutionSyncClient.h
index 43a2a0d8..7f134fb0 100644
--- a/src/core/EvolutionSyncClient.h
+++ b/src/core/EvolutionSyncClient.h
@@ -17,6 +17,12 @@
#include <stdint.h>
using namespace std;
+#include <boost/smart_ptr.hpp>
+
+namespace SyncEvolution {
+ class TransportAgent;
+}
+using namespace SyncEvolution;
class SourceList;
class EvolutionSyncSource;
#include <synthesis/sync_declarations.h>
@@ -213,6 +219,25 @@ class EvolutionSyncClient : public SyncClient, public EvolutionSyncConfig, publi
virtual void prepare(SyncSource **sources);
/**
+ * instantiate transport agent
+ *
+ * Called by engine when it needs to do HTTP POST requests. The
+ * transport agent will be used throughout the sync session and
+ * unref'ed when no longer needed. At most one agent will be
+ * requested at a time. The transport agent is intentionally
+ * returned as a Boost shared pointer so that a pointer to a
+ * class with a different life cycle is possible, either by
+ * keeping a reference or by returning a shared_ptr where the
+ * destructor doesn't do anything.
+ *
+ * The default implementation instantiates one of the builtin
+ * transport agents, depending on how it was compiled.
+ *
+ * @return transport agent
+ */
+ virtual boost::shared_ptr<TransportAgent> createTransportAgent();
+
+ /**
* display a text message from the server
*
* Not really used by SyncML servers. Could be displayed in a
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 94c1519a..16c88085 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -32,6 +32,13 @@ CORE_SOURCES = \
eds_abi_wrapper.h \
eds_abi_wrapper.cpp \
\
+ TransportAgent.h \
+ CurlTransportAgent.h \
+ CurlTransportAgent.cpp \
+ \
+ SoupTransportAgent.h \
+ SoupTransportAgent.cpp \
+ \
SyncEvolutionUtil.cpp \
SyncEvolutionUtil.h \
\
@@ -71,8 +78,8 @@ CORE_SOURCES = \
$(VOCL_SOURCES)
libsyncevolution_la_SOURCES = $(CORE_SOURCES)
-libsyncevolution_la_LIBADD = @EPACKAGE_LIBS@ @GLIB_LIBS@ @FUNAMBOL_LIBS@ @TRANSPORT_LDFLAGS@ @LIBS@ $(SYNTHESIS_LIBS)
-libsyncevolution_la_CXXFLAGS = $(SYNCEVOLUTION_CXXFLAGS) $(SYNTHESIS_CFLAGS)
+libsyncevolution_la_LIBADD = @EPACKAGE_LIBS@ @GLIB_LIBS@ @FUNAMBOL_LIBS@ $(TRANSPORT_LIBS) @LIBS@ $(SYNTHESIS_LIBS)
+libsyncevolution_la_CXXFLAGS = $(TRANSPORT_CFLAGS) $(SYNCEVOLUTION_CXXFLAGS) $(SYNTHESIS_CFLAGS)
SyncEvolutionXML.c: $(srcdir)/../syncevolution.xml
echo "const char *SyncEvolutionXML =" > $@
diff --git a/src/core/SoupTransportAgent.cpp b/src/core/SoupTransportAgent.cpp
new file mode 100644
index 00000000..3bf00d83
--- /dev/null
+++ b/src/core/SoupTransportAgent.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2009 Patrick Ohly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "SoupTransportAgent.h"
+
+#ifdef ENABLE_LIBSOUP
+
+namespace SyncEvolution {
+
+SoupTransportAgent::SoupTransportAgent() :
+ m_status(INACTIVE),
+ m_session(soup_session_async_new()),
+ m_loop(g_main_loop_new(g_main_context_default(), TRUE), "Soup main loop"),
+ m_response(NULL)
+{
+}
+
+SoupTransportAgent::~SoupTransportAgent()
+{
+}
+
+void SoupTransportAgent::setURL(const std::string &url)
+{
+ m_URL = url;
+}
+
+void SoupTransportAgent::setProxy(const std::string &proxy)
+{
+ eptr<SoupURI, SoupURI, GLibUnref> uri(soup_uri_new(proxy.c_str()), "Proxy URI");
+ g_object_set(m_session.get(),
+ SOUP_SESSION_PROXY_URI, uri.get(),
+ NULL);
+}
+
+void SoupTransportAgent::setProxyAuth(const std::string &user, const std::string &password)
+{
+ /**
+ * @TODO: handle "authenticate" signal for both proxy and HTTP server
+ * (https://sourceforge.net/tracker/index.php?func=detail&aid=2056162&group_id=146288&atid=764733).
+ *
+ * Proxy authentication is available, but still needs to be hooked up
+ * with libsoup. Should we make this interactive? Needs an additional
+ * API for TransportAgent into caller.
+ *
+ * HTTP authentication is not available.
+ */
+ m_proxyUser = user;
+ m_proxyPassword = password;
+}
+
+void SoupTransportAgent::setContentType(const std::string &type)
+{
+ m_contentType = type;
+}
+
+void SoupTransportAgent::send(const char *data, size_t len)
+{
+ // ownership is transferred to libsoup in soup_session_queue_message()
+ SoupMessage *message = soup_message_new("POST", m_URL.c_str());
+ if (!message) {
+ throw std::string("could not allocate SoupMessage");
+ }
+ soup_message_set_request(message, m_contentType.c_str(),
+ SOUP_MEMORY_TEMPORARY, data, len);
+ m_status = ACTIVE;
+ soup_session_queue_message(m_session.get(), message,
+ SessionCallback, static_cast<gpointer>(this));
+}
+
+void SoupTransportAgent::cancel()
+{
+ /** @TODO: implement if needed */
+}
+
+TransportAgent::Status SoupTransportAgent::wait()
+{
+ switch (m_status) {
+ case ACTIVE:
+ // block in main loop until our HandleSessionCallback() stops the loop
+ g_main_loop_run(m_loop.get());
+ break;
+ default:
+ break;
+ }
+ return m_status;
+}
+
+void SoupTransportAgent::getReply(const char *&data, size_t &len)
+{
+ if (m_response) {
+ data = m_response->data;
+ len = m_response->length;
+ } else {
+ data = NULL;
+ len = 0;
+ }
+}
+
+void SoupTransportAgent::SessionCallback(SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ static_cast<SoupTransportAgent *>(user_data)->HandleSessionCallback(session, msg);
+}
+
+void SoupTransportAgent::HandleSessionCallback(SoupSession *session,
+ SoupMessage *msg)
+{
+ // keep a reference to the data
+ if (msg->response_body) {
+ m_response = soup_message_body_flatten(msg->response_body);
+ } else {
+ m_response = NULL;
+ }
+ if (msg->status_code != 200) {
+ // TODO: logging of msg->reason_phrase
+ m_status = FAILED;
+ } else {
+ m_status = GOT_REPLY;
+ }
+
+ g_main_loop_quit(m_loop.get());
+}
+
+} // namespace SyncEvolution
+
+#endif // ENABLE_LIBSOUP
diff --git a/src/core/SoupTransportAgent.h b/src/core/SoupTransportAgent.h
new file mode 100644
index 00000000..f9207990
--- /dev/null
+++ b/src/core/SoupTransportAgent.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 Patrick Ohly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef INCL_SOUPTRANSPORTAGENT
+#define INCL_SOUPTRANSPORTAGENT
+
+#include <config.h>
+
+#ifdef ENABLE_LIBSOUP
+
+#include "TransportAgent.h"
+#include "EvolutionSmartPtr.h"
+#include <libsoup/soup.h>
+#include <glib/gmain.h>
+
+namespace SyncEvolution {
+
+class GLibUnref {
+ public:
+ static void unref(GMainLoop *pointer) { g_main_loop_unref(pointer); }
+ static void unref(SoupMessageBody *pointer) { soup_message_body_free(pointer); }
+ static void unref(SoupBuffer *pointer) { soup_buffer_free(pointer); }
+ static void unref(SoupURI *pointer) { soup_uri_free(pointer); }
+};
+
+/**
+ * message send/receive with libsoup
+ *
+ * An asynchronous soup session is used and the main loop
+ * is invoked in the wait() method to make progress.
+ */
+class SoupTransportAgent : public TransportAgent
+{
+ public:
+ SoupTransportAgent();
+ ~SoupTransportAgent();
+
+ virtual void setURL(const std::string &url);
+ virtual void setProxy(const std::string &proxy);
+ virtual void setProxyAuth(const std::string &user, const std::string &password);
+ virtual void setContentType(const std::string &type);
+ virtual void send(const char *data, size_t len);
+ virtual void cancel();
+ virtual Status wait();
+ virtual void getReply(const char *&data, size_t &len);
+
+ private:
+ std::string m_proxyUser;
+ std::string m_proxyPassword;
+ std::string m_URL;
+ std::string m_contentType;
+ eptr<SoupSession, GObject> m_session;
+ eptr<GMainLoop, GMainLoop, GLibUnref> m_loop;
+ Status m_status;
+
+ /** response, copied from SoupMessage */
+ eptr<SoupBuffer, SoupBuffer, GLibUnref> m_response;
+
+ /** SoupSessionCallback, redirected into user_data->HandleSessionCallback() */
+ static void SessionCallback(SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data);
+ void HandleSessionCallback(SoupSession *session,
+ SoupMessage *msg);
+};
+
+} // namespace SyncEvolution
+
+#endif // ENABLE_LIBSOUP
+#endif // INCL_TRANSPORTAGENT
diff --git a/src/core/TransportAgent.cpp b/src/core/TransportAgent.cpp
new file mode 100644
index 00000000..c2324bee
--- /dev/null
+++ b/src/core/TransportAgent.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2009 Patrick Ohly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "TransportAgent.h"
+
+static const char * const TransportAgent::m_contentTypeSyncML = "application/vnd.syncml+xml";
+static const char * const TransportAgent::m_contentTypeSyncWBXML = "application/vnd.syncml+wbxml";
+static const char * const TransportAgent::m_contentTypeURLEncoded = "application/x-www-form-urlencoded";
diff --git a/src/core/TransportAgent.h b/src/core/TransportAgent.h
new file mode 100644
index 00000000..be220277
--- /dev/null
+++ b/src/core/TransportAgent.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2009 Patrick Ohly
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef INCL_TRANSPORTAGENT
+#define INCL_TRANSPORTAGENT
+
+#include <string>
+
+namespace SyncEvolution {
+
+/**
+ * Abstract API for a message send/receive agent.
+ *
+ * The calling sequence is as follows:
+ * - set parameters for next message
+ * - start message send
+ * - optional: cancel transmission
+ * - wait for completion and reply
+ *
+ * Data to be sent is owned by caller. Data received as reply is
+ * allocated and owned by agent. Errors are reported via exceptions.
+ */
+class TransportAgent
+{
+ public:
+ /**
+ * set transport specific URL of next message
+ */
+ virtual void setURL(const std::string &url) = 0;
+
+ /**
+ * set proxy for transport, in protocol://[user@]host[:port] format
+ */
+ virtual void setProxy(const std::string &proxy) = 0;
+
+ /**
+ * set proxy user name (if not specified in proxy string)
+ * and password
+ */
+ virtual void setProxyAuth(const std::string &user,
+ const std::string &password) = 0;
+
+ /**
+ * define content type for post, see content type constants
+ */
+ virtual void setContentType(const std::string &type) = 0;
+
+ /**
+ * start sending message
+ *
+ * Memory must remain valid until reply is received or
+ * message transmission is canceled.
+ *
+ * @param data start address of data to send
+ * @param len number of bytes
+ */
+ virtual void send(const char *data, size_t len) = 0;
+
+ /**
+ * cancel an active message transmission
+ *
+ * Blocks until send buffer is no longer in use.
+ * Returns immediately if nothing pending.
+ */
+ virtual void cancel() = 0;
+
+ enum Status {
+ /**
+ * message is being sent or reply received,
+ * check again with wait()
+ */
+ ACTIVE,
+ /**
+ * received and buffered complete reply,
+ * get acces to it with getReponse()
+ */
+ GOT_REPLY,
+ /**
+ * message wasn't sent, try again with send()
+ */
+ CANCELED,
+ /**
+ * sending message has failed
+ */
+ FAILED,
+ /**
+ * unused transport, configure and use send()
+ */
+ INACTIVE
+ };
+
+ /**
+ * wait for reply
+ *
+ * Returns immediately if no transmission is pending.
+ */
+ virtual Status wait() = 0;
+
+ /**
+ * provides access to reply data
+ *
+ * Memory pointer remains valid as long as
+ * transport agent is not deleted and no other
+ * message is sent.
+ */
+ virtual void getReply(const char *&data, size_t &len) = 0;
+
+ /** SyncML in XML format */
+ static const char * const m_contentTypeSyncML;
+
+ /** SyncML in WBXML format */
+ static const char * const m_contentTypeSyncWBXML;
+
+ /** normal HTTP URL encoded */
+ static const char * const m_contentTypeURLEncoded;
+};
+
+} // namespace SyncEvolution
+
+#endif // INCL_TRANSPORTAGENT