diff options
-rw-r--r-- | HACKING | 6 | ||||
-rw-r--r-- | configure-post.in | 1 | ||||
-rw-r--r-- | configure-pre.in | 97 | ||||
-rw-r--r-- | src/core/CurlTransportAgent.cpp | 216 | ||||
-rw-r--r-- | src/core/CurlTransportAgent.h | 98 | ||||
-rw-r--r-- | src/core/EvolutionSmartPtr.h | 2 | ||||
-rw-r--r-- | src/core/EvolutionSyncClient.cpp | 78 | ||||
-rw-r--r-- | src/core/EvolutionSyncClient.h | 25 | ||||
-rw-r--r-- | src/core/Makefile.am | 11 | ||||
-rw-r--r-- | src/core/SoupTransportAgent.cpp | 142 | ||||
-rw-r--r-- | src/core/SoupTransportAgent.h | 85 | ||||
-rw-r--r-- | src/core/TransportAgent.cpp | 23 | ||||
-rw-r--r-- | src/core/TransportAgent.h | 135 |
13 files changed, 853 insertions, 66 deletions
@@ -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 |