summaryrefslogtreecommitdiffstats
path: root/qa/mockup
diff options
context:
space:
mode:
Diffstat (limited to 'qa/mockup')
-rw-r--r--qa/mockup/Makefile.am15
-rw-r--r--qa/mockup/curl-mockup.cxx504
-rw-r--r--qa/mockup/curl/curl.h153
-rw-r--r--qa/mockup/internals.hxx133
-rw-r--r--qa/mockup/mockup-config.cxx409
-rw-r--r--qa/mockup/mockup-config.h113
6 files changed, 1327 insertions, 0 deletions
diff --git a/qa/mockup/Makefile.am b/qa/mockup/Makefile.am
new file mode 100644
index 0000000..ef34e12
--- /dev/null
+++ b/qa/mockup/Makefile.am
@@ -0,0 +1,15 @@
+if !OS_WIN32
+noinst_LTLIBRARIES = libcmis-mockup.la
+
+libcmis_mockup_la_SOURCES = \
+ curl-mockup.cxx \
+ internals.hxx \
+ mockup-config.cxx \
+ mockup-config.h \
+ curl/curl.h
+
+libcmis_mockup_la_LIBADD = \
+ $(top_builddir)/src/libcmis/libcmis.la
+
+libcmis_mockup_la_LDFLAGS = -export-dynamic -no-undefined
+endif
diff --git a/qa/mockup/curl-mockup.cxx b/qa/mockup/curl-mockup.cxx
new file mode 100644
index 0000000..0a74a64
--- /dev/null
+++ b/qa/mockup/curl-mockup.cxx
@@ -0,0 +1,504 @@
+/* libcmis
+ * Version: MPL 1.1 / GPLv2+ / LGPLv2+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License or as specified alternatively below. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Major Contributor(s):
+ * Copyright (C) 2011 SUSE <cbosdonnat@suse.com>
+ *
+ *
+ * All Rights Reserved.
+ *
+ * For minor contributions see the git repository.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
+ * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
+ * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
+ * instead of those above.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sstream>
+
+#include "curl/curl.h"
+#include "internals.hxx"
+
+using namespace std;
+
+namespace mockup
+{
+ extern Configuration* config;
+}
+
+/** Code mostly coming from curl
+ */
+struct curl_slist *curl_slist_append( struct curl_slist * list, const char * data )
+{
+ struct curl_slist* new_item = ( struct curl_slist* ) malloc( sizeof( struct curl_slist ) );
+
+ if( new_item )
+ {
+ char *dupdata = strdup(data);
+ if ( dupdata )
+ {
+ new_item->next = NULL;
+ new_item->data = dupdata;
+ }
+ else
+ {
+ free(new_item);
+ return NULL;
+ }
+ }
+ else
+ return NULL;
+
+ if ( list )
+ {
+ curl_slist* last = list;
+ while ( last->next )
+ last = last->next;
+ last->next = new_item;
+ return list;
+ }
+
+ /* if this is the first item, then new_item *is* the list */
+ return new_item;
+}
+
+void curl_slist_free_all( struct curl_slist * list )
+{
+ if ( list )
+ {
+ struct curl_slist* item = list;
+ struct curl_slist* next = NULL;
+ do
+ {
+ next = item->next;
+ if ( item->data )
+ free( item->data );
+ free(item);
+ item = next;
+ } while ( next );
+ }
+}
+
+void curl_free( void * p )
+{
+ free( p );
+}
+
+CURLcode curl_global_init( long )
+{
+ return CURLE_OK;
+}
+
+CURL *curl_easy_init( void )
+{
+ return new CurlHandle();
+}
+
+void curl_easy_cleanup( CURL * curl )
+{
+ CurlHandle* handle = static_cast< CurlHandle* >( curl );
+ delete( handle );
+}
+
+void curl_easy_reset( CURL * curl )
+{
+ CurlHandle* handle = static_cast< CurlHandle * >( curl );
+ handle->reset( );
+}
+
+char *curl_easy_escape( CURL *, const char *string, int length )
+{
+ return strndup( string, length );
+}
+
+char *curl_unescape( const char *string, int length )
+{
+ return strndup( string, length );
+}
+
+char *curl_easy_unescape( CURL *, const char *string, int length, int * )
+{
+ return curl_unescape( string, length );
+}
+
+CURLcode curl_easy_setopt( CURL * curl, long option, ... )
+{
+ CurlHandle* handle = static_cast< CurlHandle * >( curl );
+
+ va_list arg;
+ va_start( arg, option );
+ switch ( option )
+ {
+ /* TODO Add support for more options */
+ case CURLOPT_POST:
+ {
+ if ( va_arg( arg, long ) )
+ handle->m_method = string( "POST" );
+ break;
+ }
+ case CURLOPT_UPLOAD:
+ {
+ if ( 0 != va_arg( arg, long ) )
+ handle->m_method = string( "PUT" );
+ break;
+ }
+ case CURLOPT_CUSTOMREQUEST:
+ handle->m_method = string( va_arg( arg, char* ) );
+ break;
+ case CURLOPT_URL:
+ {
+ handle->m_url = string( va_arg( arg, char* ) );
+ break;
+ }
+ case CURLOPT_WRITEFUNCTION:
+ {
+ handle->m_writeFn = va_arg( arg, write_callback );
+ break;
+ }
+ case CURLOPT_WRITEDATA:
+ {
+ handle->m_writeData = va_arg( arg, void* );
+ break;
+ }
+ case CURLOPT_READFUNCTION:
+ {
+ handle->m_readFn = va_arg( arg, read_callback );
+ break;
+ }
+ case CURLOPT_READDATA:
+ {
+ handle->m_readData = va_arg( arg, void* );
+ break;
+ }
+ case CURLOPT_INFILESIZE:
+ {
+ handle->m_readSize = va_arg( arg, long );
+ break;
+ }
+ case CURLOPT_HEADERFUNCTION:
+ {
+ handle->m_headersFn = va_arg( arg, headers_callback );
+ break;
+ }
+ case CURLOPT_WRITEHEADER:
+ {
+ handle->m_headersData = va_arg( arg, void* );
+ break;
+ }
+ case CURLOPT_USERNAME:
+ {
+ handle->m_username = string( va_arg( arg, char* ) );
+ break;
+ }
+ case CURLOPT_PASSWORD:
+ {
+ handle->m_password = string( va_arg( arg, char* ) );
+ break;
+ }
+ case CURLOPT_USERPWD:
+ {
+ string userpwd( va_arg( arg, char* ) );
+ size_t pos = userpwd.find( ':' );
+ if ( pos != string::npos )
+ {
+ handle->m_username = userpwd.substr( 0, pos );
+ handle->m_password = userpwd.substr( pos + 1 );
+ }
+ break;
+ }
+ case CURLOPT_PROXY:
+ {
+ // FIXME curl does some more complex things with port and type
+ handle->m_proxy = string( va_arg( arg, char* ) );
+ break;
+ }
+ case CURLOPT_NOPROXY:
+ {
+ handle->m_noProxy = string( va_arg( arg, char* ) );
+ break;
+ }
+ case CURLOPT_PROXYUSERNAME:
+ {
+ handle->m_proxyUser = string( va_arg( arg, char* ) );
+ break;
+ }
+ case CURLOPT_PROXYPASSWORD:
+ {
+ handle->m_proxyPass = string( va_arg( arg, char* ) );
+ break;
+ }
+ case CURLOPT_PROXYUSERPWD:
+ {
+ string userpwd( va_arg( arg, char* ) );
+ size_t pos = userpwd.find( ':' );
+ if ( pos != string::npos )
+ {
+ handle->m_proxyUser = userpwd.substr( 0, pos );
+ handle->m_proxyPass = userpwd.substr( pos + 1 );
+ }
+ break;
+ }
+ case CURLOPT_SSL_VERIFYHOST:
+ {
+ handle->m_verifyHost = va_arg( arg, long ) != 0;
+ break;
+ }
+ case CURLOPT_SSL_VERIFYPEER:
+ {
+ handle->m_verifyPeer = va_arg( arg, long ) != 0;
+ break;
+ }
+ case CURLOPT_CERTINFO:
+ {
+ handle->m_certInfo = va_arg( arg, long );
+ break;
+ }
+ case CURLOPT_HTTPHEADER:
+ {
+ handle->m_headers.clear();
+ struct curl_slist* headers = va_arg( arg, struct curl_slist* );
+ while ( headers != NULL )
+ {
+ handle->m_headers.push_back( string( headers->data ) );
+ headers = headers->next;
+ }
+ break;
+ }
+ default:
+ {
+ // We surely don't want to break the test for that.
+ }
+ }
+ va_end( arg );
+
+ return CURLE_OK;
+}
+
+CURLcode curl_easy_perform( CURL * curl )
+{
+ CurlHandle* handle = static_cast< CurlHandle * >( curl );
+
+ /* Fake a bad SSL Certificate? */
+ if ( !mockup::config->m_badSSLCertificate.empty( ) && handle->m_verifyPeer && handle->m_verifyHost )
+ {
+ return CURLE_SSL_CACERT;
+ }
+
+ /* Populate the certificate infos */
+ if ( handle->m_certInfo && !mockup::config->m_badSSLCertificate.empty( ) )
+ {
+ curl_slist * certData = NULL;
+ string cert( "Cert:" + mockup::config->m_badSSLCertificate );
+ char* c_cert = strdup( cert.c_str( ) );
+ certData = curl_slist_append( certData, c_cert );
+ free( c_cert );
+
+ handle->m_certs.num_of_certs = 1;
+ handle->m_certs.certinfo = ( struct curl_slist** )calloc( ( size_t )1, sizeof( struct curl_slist * ) );
+ handle->m_certs.certinfo[0] = certData;
+ }
+
+ /* Check the credentials */
+ if ( mockup::config->hasCredentials( ) &&
+ ( handle->m_username != mockup::config->m_username ||
+ handle->m_password != mockup::config->m_password ) )
+ {
+ // Send HTTP 401
+ handle->m_httpError = 401;
+ return CURLE_HTTP_RETURNED_ERROR;
+ }
+
+ // Store the requests for later verifications
+ stringstream body;
+ if ( handle->m_readFn && handle->m_readData )
+ {
+ size_t bufSize = 2048;
+ char* buf = new char[bufSize];
+
+ size_t read = 0;
+ do
+ {
+ read = handle->m_readFn( buf, 1, bufSize, handle->m_readData );
+ body.write( buf, read );
+ } while ( read == bufSize );
+
+ delete[] buf;
+ }
+
+ mockup::config->m_requests.push_back( mockup::Request( handle->m_url, handle->m_method, body.str( ), handle->m_headers ) );
+
+
+ return mockup::config->writeResponse( handle );
+}
+
+CURLcode curl_easy_getinfo( CURL * curl, long info, ... )
+{
+ CurlHandle* handle = static_cast< CurlHandle * >( curl );
+
+ va_list arg;
+ va_start( arg, info );
+ switch ( info )
+ {
+ case CURLINFO_RESPONSE_CODE:
+ {
+ long* buf = va_arg( arg, long* );
+ *buf = handle->m_httpError;
+ break;
+ }
+ case CURLINFO_CERTINFO:
+ {
+ struct curl_slist** param = va_arg( arg, struct curl_slist** );
+ if ( NULL != param )
+ {
+ union
+ {
+ struct curl_certinfo * to_certinfo;
+ struct curl_slist * to_slist;
+ } ptr;
+
+ // Fill it
+ ptr.to_certinfo = &handle->m_certs;
+ *param = ptr.to_slist;
+ }
+ break;
+ }
+ default:
+ {
+ // We surely don't want to break the test for that.
+ }
+ }
+ va_end( arg );
+
+ return CURLE_OK;
+}
+
+CurlHandle::CurlHandle( ) :
+ m_url( ),
+ m_writeFn( NULL ),
+ m_writeData( NULL ),
+ m_readFn( NULL ),
+ m_readData( NULL ),
+ m_readSize( 0 ),
+ m_headersFn( NULL ),
+ m_headersData( NULL ),
+ m_username( ),
+ m_password( ),
+ m_proxy( ),
+ m_noProxy( ),
+ m_proxyUser( ),
+ m_proxyPass( ),
+ m_verifyHost( true ),
+ m_verifyPeer( true ),
+ m_certInfo( false ),
+ m_certs( ),
+ m_httpError( 0 ),
+ m_method( "GET" ),
+ m_headers( )
+{
+}
+
+CurlHandle::CurlHandle( const CurlHandle& copy ) :
+ m_url( copy.m_url ),
+ m_writeFn( copy.m_writeFn ),
+ m_writeData( copy.m_writeData ),
+ m_readFn( copy.m_readFn ),
+ m_readData( copy.m_readData ),
+ m_readSize( copy.m_readSize ),
+ m_headersFn( copy.m_headersFn ),
+ m_headersData( copy.m_headersData ),
+ m_username( copy.m_username ),
+ m_password( copy.m_password ),
+ m_proxy( copy.m_proxy ),
+ m_noProxy( copy.m_noProxy ),
+ m_proxyUser( copy.m_proxyUser ),
+ m_proxyPass( copy.m_proxyPass ),
+ m_verifyHost( copy.m_verifyHost ),
+ m_verifyPeer( copy.m_verifyPeer ),
+ m_certInfo( copy.m_certInfo ),
+ m_certs( copy.m_certs ),
+ m_httpError( copy.m_httpError ),
+ m_method( copy.m_method ),
+ m_headers( copy.m_headers )
+{
+}
+
+CurlHandle& CurlHandle::operator=( const CurlHandle& copy )
+{
+ if ( this != &copy )
+ {
+ m_url = copy.m_url;
+ m_writeFn = copy.m_writeFn;
+ m_writeData = copy.m_writeData;
+ m_readFn = copy.m_readFn;
+ m_readData = copy.m_readData;
+ m_readSize = copy.m_readSize;
+ m_headersFn = copy.m_headersFn;
+ m_headersData = copy.m_headersData;
+ m_username = copy.m_username;
+ m_password = copy.m_password;
+ m_proxy = copy.m_proxy;
+ m_noProxy = copy.m_noProxy;
+ m_proxyUser = copy.m_proxyUser;
+ m_proxyPass = copy.m_proxyPass;
+ m_verifyHost = copy.m_verifyHost;
+ m_verifyPeer = copy.m_verifyPeer;
+ m_certInfo = copy.m_certInfo;
+ m_certs = copy.m_certs;
+ m_httpError = copy.m_httpError;
+ m_method = copy.m_method;
+ m_headers = copy.m_headers;
+ }
+ return *this;
+}
+
+CurlHandle::~CurlHandle( )
+{
+ reset();
+}
+
+void CurlHandle::reset( )
+{
+ m_url = string( );
+ m_writeFn = NULL;
+ m_writeData = NULL;
+ m_readFn = NULL;
+ m_readData = NULL;
+ m_readSize = 0;
+ m_username = string( );
+ m_password = string( );
+ m_proxy = string( );
+ m_noProxy = string( );
+ m_proxyUser = string( );
+ m_proxyPass = string( );
+ m_verifyHost = true;
+ m_verifyPeer = true;
+ m_certInfo = false;
+
+ for ( int i = 0; i < m_certs.num_of_certs; ++i )
+ {
+ curl_slist_free_all( m_certs.certinfo[i] );
+ m_certs.certinfo[i] = NULL;
+ }
+ free( m_certs.certinfo );
+ m_certs.certinfo = NULL;
+ m_certs.num_of_certs = 0;
+
+ m_method = "GET";
+ m_headers.clear( );
+}
diff --git a/qa/mockup/curl/curl.h b/qa/mockup/curl/curl.h
new file mode 100644
index 0000000..50f1cfe
--- /dev/null
+++ b/qa/mockup/curl/curl.h
@@ -0,0 +1,153 @@
+/* libcmis
+ * Version: MPL 1.1 / GPLv2+ / LGPLv2+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License or as specified alternatively below. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Major Contributor(s):
+ * Copyright (C) 2011 SUSE <cbosdonnat@suse.com>
+ *
+ *
+ * All Rights Reserved.
+ *
+ * For minor contributions see the git repository.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
+ * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
+ * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
+ * instead of those above.
+ */
+#ifndef _MOCKUP_CURL_CURL_H_
+#define _MOCKUP_CURL_CURL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Curl used symbols to mockup */
+
+typedef void CURL;
+
+typedef enum
+{
+ CURLIOE_OK, /* I/O operation successful */
+ CURLIOE_UNKNOWNCMD, /* command was unknown to callback */
+ CURLIOE_FAILRESTART, /* failed to restart the read */
+ CURLIOE_LAST /* never use */
+} curlioerr;
+
+#define CURL_GLOBAL_SSL (1<<0)
+#define CURL_GLOBAL_WIN32 (1<<1)
+#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32)
+
+#define CURLOPTTYPE_LONG 0
+#define CURLOPTTYPE_OBJECTPOINT 10000
+#define CURLOPTTYPE_FUNCTIONPOINT 20000
+#define CURLOPTTYPE_OFF_T 30000
+
+typedef enum
+{
+ CURLOPT_WRITEFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 11,
+ CURLOPT_READFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 12,
+ CURLOPT_WRITEDATA = CURLOPTTYPE_OBJECTPOINT + 1,
+ CURLOPT_HEADERFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 79,
+ CURLOPT_WRITEHEADER = CURLOPTTYPE_OBJECTPOINT + 29,
+ CURLOPT_FOLLOWLOCATION = CURLOPTTYPE_LONG + 52,
+ CURLOPT_MAXREDIRS = CURLOPTTYPE_LONG + 68,
+ CURLOPT_INFILESIZE = CURLOPTTYPE_LONG + 14,
+ CURLOPT_READDATA = CURLOPTTYPE_OBJECTPOINT + 9,
+ CURLOPT_UPLOAD = CURLOPTTYPE_LONG + 46,
+ CURLOPT_IOCTLFUNCTION = CURLOPTTYPE_FUNCTIONPOINT + 130,
+ CURLOPT_IOCTLDATA = CURLOPTTYPE_OBJECTPOINT + 131,
+ CURLOPT_HTTPHEADER = CURLOPTTYPE_OBJECTPOINT + 23,
+ CURLOPT_POSTFIELDSIZE = CURLOPTTYPE_LONG + 60,
+ CURLOPT_POST = CURLOPTTYPE_LONG + 47,
+ CURLOPT_CUSTOMREQUEST = CURLOPTTYPE_OBJECTPOINT + 36,
+ CURLOPT_URL = CURLOPTTYPE_OBJECTPOINT + 2,
+ CURLOPT_HTTPAUTH = CURLOPTTYPE_LONG + 107,
+ CURLOPT_USERNAME = CURLOPTTYPE_OBJECTPOINT + 173,
+ CURLOPT_PASSWORD = CURLOPTTYPE_OBJECTPOINT + 174,
+ CURLOPT_USERPWD = CURLOPTTYPE_OBJECTPOINT + 5,
+ CURLOPT_ERRORBUFFER = CURLOPTTYPE_OBJECTPOINT + 10,
+ CURLOPT_FAILONERROR = CURLOPTTYPE_LONG + 45,
+ CURLOPT_VERBOSE = CURLOPTTYPE_LONG + 41,
+ CURLOPT_PROXY = CURLOPTTYPE_OBJECTPOINT + 4,
+ CURLOPT_PROXYUSERPWD = CURLOPTTYPE_OBJECTPOINT + 6,
+ CURLOPT_PROXYAUTH = CURLOPTTYPE_LONG + 111,
+ CURLOPT_PROXYUSERNAME = CURLOPTTYPE_OBJECTPOINT + 175,
+ CURLOPT_PROXYPASSWORD = CURLOPTTYPE_OBJECTPOINT + 176,
+ CURLOPT_NOPROXY = CURLOPTTYPE_OBJECTPOINT + 177,
+ CURLOPT_SSL_VERIFYPEER = CURLOPTTYPE_LONG + 64,
+ CURLOPT_SSL_VERIFYHOST = CURLOPTTYPE_LONG + 81,
+ CURLOPT_CERTINFO = CURLOPTTYPE_LONG + 172
+} CURLoption;
+
+#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4)
+#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE)
+
+typedef enum
+{
+ CURLE_OK = 0,
+ CURLE_HTTP_RETURNED_ERROR = 22,
+ CURLE_SSL_CACERT = 60,
+ /* TODO Add some more error codes from curl? */
+ CURL_LAST
+} CURLcode;
+
+struct curl_slist
+{
+ char *data;
+ struct curl_slist *next;
+};
+
+struct curl_slist *curl_slist_append( struct curl_slist *, const char * );
+void curl_slist_free_all( struct curl_slist * );
+
+void curl_free( void *p );
+CURLcode curl_global_init( long flags );
+
+CURL *curl_easy_init( void );
+void curl_easy_cleanup( CURL *curl );
+CURLcode curl_easy_setopt( CURL *curl, long option, ... );
+char *curl_easy_escape( CURL *handle, const char *string, int length );
+char *curl_unescape( const char *string, int length );
+char *curl_easy_unescape( CURL *handle, const char *string, int length, int *outlength );
+CURLcode curl_easy_perform( CURL *curl );
+void curl_easy_reset( CURL *curl );
+
+struct curl_certinfo
+{
+ int num_of_certs;
+ struct curl_slist **certinfo;
+};
+
+#define CURLINFO_LONG 0x200000
+#define CURLINFO_SLIST 0x400000
+
+typedef enum
+{
+ CURLINFO_NONE,
+ CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2,
+ CURLINFO_CERTINFO = CURLINFO_SLIST + 34,
+ CURLINFO_LASTONE = 42
+} CURLINFO;
+
+CURLcode curl_easy_getinfo( CURL *curl, long info, ... );
+
+#define LIBCURL_VERSION_MAJOR 7
+#define LIBCURL_VERSION_MINOR 26
+#define LIBCURL_VERSION_PATCH 0
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/qa/mockup/internals.hxx b/qa/mockup/internals.hxx
new file mode 100644
index 0000000..43c001e
--- /dev/null
+++ b/qa/mockup/internals.hxx
@@ -0,0 +1,133 @@
+/* libcmis
+ * Version: MPL 1.1 / GPLv2+ / LGPLv2+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License or as specified alternatively below. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Major Contributor(s):
+ * Copyright (C) 2011 SUSE <cbosdonnat@suse.com>
+ *
+ *
+ * All Rights Reserved.
+ *
+ * For minor contributions see the git repository.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
+ * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
+ * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
+ * instead of those above.
+ */
+
+#ifndef INCLUDED_QA_MOCKUP_INTERNALS_HXX
+#define INCLUDED_QA_MOCKUP_INTERNALS_HXX
+
+#include <map>
+#include <string>
+#include <vector>
+
+typedef size_t ( *write_callback )( char *ptr, size_t size, size_t nmemb, void *userdata );
+typedef size_t ( *read_callback )( char *ptr, size_t size, size_t nmemb, void *userdata );
+typedef size_t ( *headers_callback )( char *ptr, size_t size, size_t nmemb, void *userdata );
+
+class CurlHandle
+{
+ public:
+ CurlHandle( );
+ CurlHandle( const CurlHandle& copy );
+ CurlHandle& operator=( const CurlHandle& copy );
+ ~CurlHandle( );
+
+ std::string m_url;
+
+ write_callback m_writeFn;
+ void* m_writeData;
+ read_callback m_readFn;
+ void* m_readData;
+ long m_readSize;
+ headers_callback m_headersFn;
+ void* m_headersData;
+
+ std::string m_username;
+ std::string m_password;
+ std::string m_proxy;
+ std::string m_noProxy;
+ std::string m_proxyUser;
+ std::string m_proxyPass;
+
+ bool m_verifyHost;
+ bool m_verifyPeer;
+ bool m_certInfo;
+
+ struct curl_certinfo m_certs;
+
+ long m_httpError;
+ std::string m_method;
+ std::vector< std::string > m_headers;
+
+ void reset( );
+};
+
+namespace mockup
+{
+ class Response
+ {
+ public:
+ Response( std::string response, unsigned int status, bool isFilePath,
+ std::string headers );
+
+ std::string m_response;
+ unsigned int m_status;
+ bool m_isFilePath;
+ std::string m_headers;
+ };
+
+ class Request
+ {
+ public:
+ Request( std::string url, std::string method, std::string body,
+ std::vector< std::string > headers );
+
+ std::string m_url;
+ std::string m_method;
+ std::string m_body;
+ std::vector< std::string > m_headers;
+ };
+
+ class RequestMatcher
+ {
+ public:
+ RequestMatcher( std::string baseUrl, std::string matchParam,
+ std::string method, std::string matchBody );
+ bool operator< ( const RequestMatcher& compare ) const;
+
+ std::string m_baseUrl;
+ std::string m_matchParam;
+ std::string m_method;
+ std::string m_matchBody;
+ };
+
+ class Configuration
+ {
+ public:
+ Configuration( );
+
+ bool hasCredentials( );
+ CURLcode writeResponse( CurlHandle* handle );
+
+ std::map< RequestMatcher, Response > m_responses;
+ std::vector< Request > m_requests;
+ std::string m_username;
+ std::string m_password;
+ std::string m_badSSLCertificate;
+ };
+}
+
+#endif
diff --git a/qa/mockup/mockup-config.cxx b/qa/mockup/mockup-config.cxx
new file mode 100644
index 0000000..7678518
--- /dev/null
+++ b/qa/mockup/mockup-config.cxx
@@ -0,0 +1,409 @@
+/* libcmis
+ * Version: MPL 1.1 / GPLv2+ / LGPLv2+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License or as specified alternatively below. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Major Contributor(s):
+ * Copyright (C) 2011 SUSE <cbosdonnat@suse.com>
+ *
+ *
+ * All Rights Reserved.
+ *
+ * For minor contributions see the git repository.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
+ * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
+ * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
+ * instead of those above.
+ */
+
+#include "mockup-config.h"
+
+#include <memory>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <iostream>
+
+#include <boost/algorithm/string.hpp>
+
+#include "internals.hxx"
+
+using namespace std;
+
+namespace
+{
+ void lcl_splitUrl( const string& url, string& urlBase, string& params )
+ {
+ size_t pos = url.find( "?" );
+ urlBase = url;
+ if ( pos != string::npos )
+ {
+ urlBase = url.substr( 0, pos );
+ params = url.substr( pos + 1 );
+ }
+ }
+
+ const char** lcl_toStringArray( const vector< string >& vect )
+ {
+ const char** array = new const char*[vect.size() + 1];
+ for ( size_t i = 0; i < vect.size( ); i++ )
+ array[i] = vect[i].c_str();
+ array[vect.size()] = NULL;
+ return array;
+ }
+}
+
+namespace mockup
+{
+ Response::Response( string response, unsigned int status, bool isFilePath, string headers ) :
+ m_response( response ),
+ m_status( status ),
+ m_isFilePath( isFilePath ),
+ m_headers( headers )
+ {
+ }
+
+ Request::Request( string url, string method, string body, vector< string > headers ) :
+ m_url( url ),
+ m_method( method ),
+ m_body( body ),
+ m_headers( headers )
+ {
+ }
+
+ RequestMatcher::RequestMatcher( string baseUrl, string matchParam, string method, string matchBody ) :
+ m_baseUrl( baseUrl ),
+ m_matchParam( matchParam ),
+ m_method( method ),
+ m_matchBody( matchBody )
+ {
+ }
+
+ bool RequestMatcher::operator< ( const RequestMatcher& compare ) const
+ {
+ int cmpBaseUrl = m_baseUrl.compare( compare.m_baseUrl ) ;
+ if ( cmpBaseUrl != 0 )
+ return cmpBaseUrl < 0;
+
+ int cmpMatchParam = m_matchParam.compare( compare.m_matchParam );
+ if ( cmpMatchParam != 0 )
+ return cmpMatchParam < 0;
+
+ int cmpMatchMethod = m_method.compare( compare.m_method );
+ if ( cmpMatchMethod != 0 )
+ return cmpMatchMethod < 0;
+
+ int cmpMatchBody = m_matchBody.compare( compare.m_matchBody );
+ return cmpMatchBody < 0;
+ }
+
+ Configuration::Configuration( ) :
+ m_responses( ),
+ m_requests( ),
+ m_username( ),
+ m_password( ),
+ m_badSSLCertificate( )
+ {
+ }
+
+ bool Configuration::hasCredentials( )
+ {
+ return !m_username.empty( ) && !m_password.empty( );
+ }
+
+ /** Find a suitable response
+ * using the request as a search key
+ */
+ CURLcode Configuration::writeResponse( CurlHandle* handle )
+ {
+ CURLcode code = CURLE_OK;
+
+ string headers;
+ string response;
+ bool foundResponse = false;
+ bool isFilePath = true;
+ const string& url = handle->m_url;
+
+ string urlBase = url;
+ string params;
+ lcl_splitUrl( url, urlBase, params );
+ string method = handle->m_method;
+ string body = m_requests.back().m_body;
+
+ for ( map< RequestMatcher, Response >::iterator it = m_responses.begin( );
+ it != m_responses.end( ) && response.empty( ); ++it )
+ {
+ RequestMatcher matcher = it->first;
+ string& paramFind = matcher.m_matchParam;
+ bool matchBaseUrl = matcher.m_baseUrl.empty() || ( urlBase == matcher.m_baseUrl );
+ bool matchParams = paramFind.empty( ) || ( params.find( paramFind ) != string::npos );
+ bool matchMethod = it->first.m_method.empty( ) || ( it->first.m_method == method );
+ bool matchBody = matcher.m_matchBody.empty( ) || ( body.find( matcher.m_matchBody ) != string::npos );
+
+ if ( matchBaseUrl && matchParams && matchMethod && matchBody )
+ {
+ foundResponse = true;
+ response = it->second.m_response;
+ handle->m_httpError = it->second.m_status;
+ isFilePath = it->second.m_isFilePath;
+ headers = it->second.m_headers;
+ }
+ }
+
+ // Output headers is any
+ if ( !headers.empty() )
+ {
+ char* buf = strdup( headers.c_str() );
+ handle->m_headersFn( buf, 1, headers.size( ), handle->m_headersData );
+ free( buf );
+ }
+
+ // If nothing matched, then send a 404 HTTP error instead
+ if ( !foundResponse || ( foundResponse && isFilePath && response.empty() ) )
+ handle->m_httpError = 404;
+ else
+ {
+ if ( isFilePath )
+ {
+ FILE* fd = fopen( response.c_str( ), "r" );
+ if ( !fd ) {
+ cerr << "Missing test file: " << response << endl;
+ handle->m_httpError = 500;
+ return CURLE_HTTP_RETURNED_ERROR;
+ }
+
+
+ size_t bufSize = 2048;
+ char* buf = new char[bufSize];
+
+ size_t read = 0;
+ size_t written = 0;
+ do
+ {
+ read = fread( buf, 1, bufSize, fd );
+ written = handle->m_writeFn( buf, 1, read, handle->m_writeData );
+ } while ( read == bufSize && written == read );
+
+ fclose( fd );
+ delete[] buf;
+ }
+ else
+ {
+ if ( !response.empty() )
+ {
+ char* buf = strdup( response.c_str() );
+ handle->m_writeFn( buf, 1, response.size( ), handle->m_writeData );
+ free( buf );
+ }
+ }
+ }
+
+ // What curl error code to give?
+ if ( handle->m_httpError == 0 )
+ handle->m_httpError = 200;
+
+ if ( handle->m_httpError < 200 || handle->m_httpError >= 300 )
+ code = CURLE_HTTP_RETURNED_ERROR;
+
+ return code;
+ }
+
+ unique_ptr<Configuration> config{ new Configuration( ) };
+}
+
+void curl_mockup_reset( )
+{
+ mockup::config.reset( new mockup::Configuration( ) );
+}
+
+void curl_mockup_addResponse( const char* urlBase, const char* matchParam, const char* method,
+ const char* response, unsigned int status, bool isFilePath,
+ const char* headers, const char* matchBody )
+{
+ string matchBodyStr;
+ if ( matchBody )
+ matchBodyStr = matchBody;
+ mockup::RequestMatcher matcher( urlBase, matchParam, method, matchBodyStr );
+ map< mockup::RequestMatcher, mockup::Response >::iterator it = mockup::config->m_responses.find( matcher );
+ if ( it != mockup::config->m_responses.end( ) )
+ mockup::config->m_responses.erase( it );
+
+ string headersStr;
+ if ( headers != NULL )
+ headersStr = headers;
+ mockup::Response responseDesc( response, status, isFilePath, headersStr );
+ mockup::config->m_responses.insert( pair< mockup::RequestMatcher, mockup::Response >( matcher, responseDesc ) );
+}
+
+void curl_mockup_setResponse( const char* filepath )
+{
+ mockup::config->m_responses.clear( );
+ curl_mockup_addResponse( "", "", "", filepath );
+}
+
+void curl_mockup_setCredentials( const char* username, const char* password )
+{
+ mockup::config->m_username = string( username );
+ mockup::config->m_password = string( password );
+}
+
+const struct HttpRequest* curl_mockup_getRequest( const char* urlBase,
+ const char* matchParam,
+ const char* method,
+ const char* matchBody )
+{
+ struct HttpRequest* request = NULL;
+
+ string urlBaseString( urlBase );
+ string matchParamString( matchParam );
+
+ string matchBodyStr;
+ if ( matchBody )
+ matchBodyStr = matchBody;
+
+ for ( vector< mockup::Request >::iterator it = mockup::config->m_requests.begin( );
+ it != mockup::config->m_requests.end( ) && request == NULL; ++it )
+ {
+ string url;
+ string params;
+ if ( it->m_method == string( method ) )
+ {
+ lcl_splitUrl( it->m_url, url, params );
+
+ bool matchBaseUrl = urlBaseString.empty() || boost::starts_with( url, urlBaseString );
+ bool matchParams = matchParamString.empty( ) || ( params.find( matchParamString ) != string::npos );
+ bool matchBodyPart = !matchBody || ( it->m_body.find( matchBodyStr ) != string::npos );
+
+ if ( matchBaseUrl && matchParams && matchBodyPart )
+ {
+ request = new HttpRequest;
+ request->url = it->m_url.c_str();
+ request->body = it->m_body.c_str();
+ request->headers = lcl_toStringArray( it->m_headers );
+ }
+ }
+ }
+
+ return request;
+}
+
+const char* curl_mockup_getRequestBody( const char* urlBase,
+ const char* matchParam,
+ const char* method,
+ const char* matchBody )
+{
+ const struct HttpRequest* request = curl_mockup_getRequest( urlBase, matchParam, method, matchBody );
+ if ( request )
+ {
+ const char* body = request->body;
+ curl_mockup_HttpRequest_free( request );
+ return body;
+ }
+ return NULL;
+}
+
+int curl_mockup_getRequestsCount( const char* urlBase,
+ const char* matchParam,
+ const char* method,
+ const char* matchBody )
+{
+ int count = 0;
+
+ string urlBaseString( urlBase );
+ string matchParamString( matchParam );
+ string matchBodyStr( matchBody );
+
+ for ( vector< mockup::Request >::iterator it = mockup::config->m_requests.begin( );
+ it != mockup::config->m_requests.end( ); ++it )
+ {
+ string url;
+ string params;
+ if ( it->m_method == string( method ) )
+ {
+ lcl_splitUrl( it->m_url, url, params );
+
+ bool matchBaseUrl = urlBaseString.empty() || boost::starts_with( url, urlBaseString );
+ bool matchParams = matchParamString.empty( ) ||
+ ( params.find( matchParamString ) != string::npos );
+ bool matchBodyPart = matchBodyStr.empty() ||
+ ( it->m_body.find( matchBodyStr ) != string::npos );
+
+ if ( matchBaseUrl && matchParams && matchBodyPart )
+ {
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
+void curl_mockup_HttpRequest_free( const struct HttpRequest* request )
+{
+ delete[] request->headers;
+ delete request;
+}
+
+char* curl_mockup_HttpRequest_getHeader( const struct HttpRequest* request, const char* name )
+{
+ char* value = NULL;
+ size_t i = 0;
+ while ( request->headers[i] != NULL && value == NULL )
+ {
+ string header = request->headers[i];
+ const string prefix = string( name ) + ":";
+ size_t pos = header.find( prefix );
+ if ( pos == 0 )
+ {
+ value = strdup( header.substr( prefix.size() ).c_str() );
+ }
+ ++i;
+ }
+ return value;
+}
+
+const char* curl_mockup_getProxy( CURL* curl )
+{
+ CurlHandle* handle = static_cast< CurlHandle* >( curl );
+ if ( NULL != handle )
+ return handle->m_proxy.c_str();
+ return NULL;
+}
+
+const char* curl_mockup_getNoProxy( CURL* curl )
+{
+ CurlHandle* handle = static_cast< CurlHandle* >( curl );
+ if ( NULL != handle )
+ return handle->m_noProxy.c_str();
+ return NULL;
+}
+
+const char* curl_mockup_getProxyUser( CURL* curl )
+{
+ CurlHandle* handle = static_cast< CurlHandle* >( curl );
+ if ( NULL != handle )
+ return handle->m_proxyUser.c_str();
+ return NULL;
+}
+
+const char* curl_mockup_getProxyPass( CURL* curl )
+{
+ CurlHandle* handle = static_cast< CurlHandle* >( curl );
+ if ( NULL != handle )
+ return handle->m_proxyPass.c_str();
+ return NULL;
+}
+
+void curl_mockup_setSSLBadCertificate( const char* certificate )
+{
+ mockup::config->m_badSSLCertificate = string( certificate );
+}
diff --git a/qa/mockup/mockup-config.h b/qa/mockup/mockup-config.h
new file mode 100644
index 0000000..d0fc3bb
--- /dev/null
+++ b/qa/mockup/mockup-config.h
@@ -0,0 +1,113 @@
+/* libcmis
+ * Version: MPL 1.1 / GPLv2+ / LGPLv2+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License or as specified alternatively below. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Major Contributor(s):
+ * Copyright (C) 2011 SUSE <cbosdonnat@suse.com>
+ *
+ *
+ * All Rights Reserved.
+ *
+ * For minor contributions see the git repository.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
+ * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
+ * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
+ * instead of those above.
+ */
+
+#include <curl/curl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Mockup behavior configuration functions */
+void curl_mockup_reset( );
+
+/** Add a new HTTP response the server should send.
+
+ \param baseURL
+ the base URL of the request without parameters
+ \param matchParam
+ a string to find in the parameters part of the URL to match
+ \param method
+ HTTP method to match like PUT, GET, POST or DELETE. An empty
+ string matches any method.
+ \param response
+ a string corresponding either to the file path of the request
+ body to send or directly the content to send. This value has
+ a different meaning depending on isFilePath parameter.
+ \param status
+ the HTTP status to return. 0 means HTTP OK (200).
+ \param isFilePath
+ if this value is true the response value is used as a file path,
+ otherwise, the response value is used as the body of the HTTP
+ response to send.
+ \param headers
+ the HTTP headers block to send with the response. By default
+ no header is sent.
+ \param matchBody
+ a string to find in the request body to match
+ */
+void curl_mockup_addResponse( const char* baseUrl, const char* matchParam, const char* method,
+ const char* response, unsigned int status = 0, bool isFilePath = true,
+ const char* headers = 0, const char* matchBody = 0 );
+
+/** Set the HTTP response the server is supposed to send.
+ This will reset all already defined responses.
+ */
+void curl_mockup_setResponse( const char* filepath );
+void curl_mockup_setCredentials( const char* username, const char* password );
+
+struct HttpRequest
+{
+ const char* url;
+ const char* body;
+ ///< NULL terminated array of headers.
+ const char** headers;
+};
+
+const struct HttpRequest* curl_mockup_getRequest( const char* baseUrl,
+ const char* matchParam,
+ const char* method,
+ const char* matchBody = 0 );
+const char* curl_mockup_getRequestBody( const char* baseUrl,
+ const char* matchParam,
+ const char* method,
+ const char* matchBody = 0 );
+int curl_mockup_getRequestsCount( const char* urlBase,
+ const char* matchParam,
+ const char* method,
+ const char* matchBody = "" );
+
+void curl_mockup_HttpRequest_free( const struct HttpRequest* request );
+
+/** The resulting value is either NULL (no such header found) or the value
+ of the header. In such a case, the result needs to be freed by the caller.
+ */
+char* curl_mockup_HttpRequest_getHeader( const struct HttpRequest* request, const char* name );
+
+const char* curl_mockup_getProxy( CURL* handle );
+const char* curl_mockup_getNoProxy( CURL* handle );
+const char* curl_mockup_getProxyUser( CURL* handle );
+const char* curl_mockup_getProxyPass( CURL* handle );
+
+/** Set a fake invalid certificate to raise CURLE_SSL_CACERT. Setting it
+ to an empty string will reset to no certificate.
+ */
+void curl_mockup_setSSLBadCertificate( const char* certificate );
+
+#ifdef __cplusplus
+}
+#endif