Update copyright.
[shibboleth/cpp-xmltooling.git] / xmltooling / soap / impl / CURLSOAPTransport.cpp
index 8b2aad3..7fa8704 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2006 Internet2
+ *  Copyright 2001-2007 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -68,8 +68,10 @@ namespace xmltooling {
     public:
         CURLSOAPTransport(const KeyInfoSource& peer, const char* endpoint)
                 : m_peer(peer), m_endpoint(endpoint), m_handle(NULL), m_headers(NULL),
-                    m_credResolver(NULL), m_trustEngine(NULL), m_keyResolver(NULL),
-                    m_ssl_callback(NULL), m_ssl_userptr(NULL) {
+#ifndef XMLTOOLING_NO_XMLSEC
+                    m_credResolver(NULL), m_trustEngine(NULL), m_mandatory(false), m_keyResolver(NULL),
+#endif
+                    m_ssl_callback(NULL), m_ssl_userptr(NULL), m_secure(false) {
             m_handle = g_CURLPool->get(peer.getName(), endpoint);
             curl_easy_setopt(m_handle,CURLOPT_URL,endpoint);
             curl_easy_setopt(m_handle,CURLOPT_CONNECTTIMEOUT,15);
@@ -77,11 +79,14 @@ namespace xmltooling {
             curl_easy_setopt(m_handle,CURLOPT_HTTPAUTH,0);
             curl_easy_setopt(m_handle,CURLOPT_USERPWD,NULL);
             curl_easy_setopt(m_handle,CURLOPT_HEADERDATA,this);
+            m_headers=curl_slist_append(m_headers,"Content-Type: text/xml");
+            m_headers=curl_slist_append(m_headers,"Transport-Encoding: chunked");
         }
         
         virtual ~CURLSOAPTransport() {
             curl_slist_free_all(m_headers);
             curl_easy_setopt(m_handle,CURLOPT_ERRORBUFFER,NULL);
+            curl_easy_setopt(m_handle,CURLOPT_PRIVATE,m_secure ? "secure" : NULL); // Save off security "state".
             g_CURLPool->put(m_peer.getName(), m_endpoint.c_str(), m_handle);
         }
 
@@ -95,6 +100,7 @@ namespace xmltooling {
         
         bool setAuth(transport_auth_t authType, const char* username=NULL, const char* password=NULL) const;
         
+#ifndef XMLTOOLING_NO_XMLSEC
         bool setCredentialResolver(const CredentialResolver* credResolver) const {
             const OpenSSLCredentialResolver* down = dynamic_cast<const OpenSSLCredentialResolver*>(credResolver);
             if (!down) {
@@ -105,7 +111,7 @@ namespace xmltooling {
             return true;
         }
         
-        bool setTrustEngine(const X509TrustEngine* trustEngine, const KeyResolver* keyResolver=NULL) const {
+        bool setTrustEngine(const X509TrustEngine* trustEngine, bool mandatory=true, const KeyResolver* keyResolver=NULL) const {
             const OpenSSLTrustEngine* down = dynamic_cast<const OpenSSLTrustEngine*>(trustEngine);
             if (!down) {
                 m_trustEngine = NULL;
@@ -114,11 +120,26 @@ namespace xmltooling {
             }
             m_trustEngine = down;
             m_keyResolver = keyResolver;
+            m_mandatory = mandatory;
             return true;
         }
         
-        size_t send(istream& in, ostream& out);
+#endif
+        
+        void send(istream& in);
+        
+        istream& receive() {
+            return m_stream;
+        }
         
+        bool isSecure() const {
+            return m_secure;
+        }
+
+        void setSecure(bool secure) {
+            m_secure = secure;
+        }
+
         string getContentType() const;
         
         bool setRequestHeader(const char* name, const char* val) const {
@@ -141,13 +162,18 @@ namespace xmltooling {
         const KeyInfoSource& m_peer;
         string m_endpoint;
         CURL* m_handle;
+        stringstream m_stream;
         mutable struct curl_slist* m_headers;
         map<string,vector<string> > m_response_headers;
+#ifndef XMLTOOLING_NO_XMLSEC
         mutable const OpenSSLCredentialResolver* m_credResolver;
         mutable const OpenSSLTrustEngine* m_trustEngine;
+        mutable bool m_mandatory;
         mutable const KeyResolver* m_keyResolver;
+#endif
         mutable ssl_ctx_callback_fn m_ssl_callback;
         mutable void* m_ssl_userptr;
+        bool m_secure;
         
         friend size_t XMLTOOL_DLLLOCAL curl_header_hook(void* ptr, size_t size, size_t nmemb, void* stream);
         friend CURLcode XMLTOOL_DLLLOCAL xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* userptr);
@@ -160,7 +186,9 @@ namespace xmltooling {
     size_t XMLTOOL_DLLLOCAL curl_read_hook( void *ptr, size_t size, size_t nmemb, void *stream);
     int XMLTOOL_DLLLOCAL curl_debug_hook(CURL* handle, curl_infotype type, char* data, size_t len, void* ptr);
     CURLcode XMLTOOL_DLLLOCAL xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* userptr);
+#ifndef XMLTOOLING_NO_XMLSEC
     int XMLTOOL_DLLLOCAL verify_callback(X509_STORE_CTX* x509_ctx, void* arg);
+#endif
 
     SOAPTransport* CURLSOAPTransportFactory(const pair<const KeyInfoSource*,const char*>& dest)
     {
@@ -325,7 +353,7 @@ string CURLSOAPTransport::getContentType() const
     return content_type ? content_type : "";
 }
 
-size_t CURLSOAPTransport::send(istream& in, ostream& out)
+void CURLSOAPTransport::send(istream& in)
 {
 #ifdef _DEBUG
     xmltooling::NDC ndc("send");
@@ -337,11 +365,9 @@ size_t CURLSOAPTransport::send(istream& in, ostream& out)
     // caller should have executed any set functions to manipulate it.
 
     // Setup standard per-call curl properties.
-    size_t content_length=0;
-    pair<ostream*,size_t*> output = make_pair(&out,&content_length); 
     curl_easy_setopt(m_handle,CURLOPT_POST,1);
     curl_easy_setopt(m_handle,CURLOPT_READDATA,&in);
-    curl_easy_setopt(m_handle,CURLOPT_FILE,&output);
+    curl_easy_setopt(m_handle,CURLOPT_FILE,&m_stream);
     curl_easy_setopt(m_handle,CURLOPT_DEBUGDATA,&log_curl);
 
     char curl_errorbuf[CURL_ERROR_SIZE];
@@ -356,6 +382,13 @@ size_t CURLSOAPTransport::send(istream& in, ostream& out)
     if (m_ssl_callback || m_credResolver || m_trustEngine) {
         curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,xml_ssl_ctx_callback);
         curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_DATA,this);
+
+        // Restore security "state". Necessary because the callback only runs
+        // when handshakes occur. Even new TCP connections won't execute it.
+        char* priv=NULL;
+        curl_easy_getinfo(m_handle,CURLINFO_PRIVATE,&priv);
+        if (priv)
+            m_secure=true;
     }
     else {
         curl_easy_setopt(m_handle,CURLOPT_SSL_CTX_FUNCTION,NULL);
@@ -374,8 +407,6 @@ size_t CURLSOAPTransport::send(istream& in, ostream& out)
             string("CURLSOAPTransport::send() failed while contacting SOAP responder: ") +
                 (curl_errorbuf[0] ? curl_errorbuf : "no further information available"));
     }
-    
-    return content_length;
 }
 
 // callback to buffer headers from server
@@ -417,10 +448,8 @@ size_t xmltooling::curl_read_hook(void* ptr, size_t size, size_t nmemb, void* st
 // callback to buffer data from server
 size_t xmltooling::curl_write_hook(void* ptr, size_t size, size_t nmemb, void* stream)
 {
-    pair<ostream*,size_t*>* output = reinterpret_cast<pair<ostream*,size_t*>*>(stream); 
     size_t len = size*nmemb;
-    output->first->write(reinterpret_cast<const char*>(ptr),len);
-    *(output->second) += len;
+    reinterpret_cast<stringstream*>(stream)->write(reinterpret_cast<const char*>(ptr),len);
     return len;
 }
 
@@ -436,6 +465,7 @@ int xmltooling::curl_debug_hook(CURL* handle, curl_infotype type, char* data, si
     return 0;
 }
 
+#ifndef XMLTOOLING_NO_XMLSEC
 int xmltooling::verify_callback(X509_STORE_CTX* x509_ctx, void* arg)
 {
     Category::getInstance("OpenSSL").debug("invoking X509 verify callback");
@@ -456,17 +486,22 @@ int xmltooling::verify_callback(X509_STORE_CTX* x509_ctx, void* arg)
      // Bypass name check (handled for us by curl).
     if (!ctx->m_trustEngine->validate(x509_ctx->cert,x509_ctx->untrusted,ctx->m_peer,false,ctx->m_keyResolver)) {
         x509_ctx->error=X509_V_ERR_APPLICATION_VERIFICATION;     // generic error, check log for plugin specifics
-        return 0;
+        ctx->setSecure(false);
+        return ctx->m_mandatory ? 0 : 1;
     }
     
     // Signal success. Hopefully it doesn't matter what's actually in the structure now.
+    ctx->setSecure(true);
     return 1;
 }
+#endif
 
 // callback to invoke a caller-defined SSL callback
 CURLcode xmltooling::xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* userptr)
 {
     CURLSOAPTransport* conf = reinterpret_cast<CURLSOAPTransport*>(userptr);
+
+#ifndef XMLTOOLING_NO_XMLSEC
     if (conf->m_credResolver)
         conf->m_credResolver->attach(ssl_ctx);
 
@@ -483,8 +518,9 @@ CURLcode xmltooling::xml_ssl_ctx_callback(CURL* curl, SSL_CTX* ssl_ctx, void* us
         SSL_CTX_set_verify_depth(ssl_ctx,reinterpret_cast<int>(userptr));
 #endif
     }
+#endif
         
-    if (!conf->m_ssl_callback(ssl_ctx,conf->m_ssl_userptr))
+    if (!conf->m_ssl_callback(conf, ssl_ctx, conf->m_ssl_userptr))
         return CURLE_SSL_CERTPROBLEM;
         
     return CURLE_OK;