[couchdb-glib] Added OAuth support:



commit fad375b6d7d27469fbfdbb1b15d8e070511d294d
Author: Rodrigo Moya <rodrigo gnome-db org>
Date:   Wed Sep 9 16:53:00 2009 +0200

    Added OAuth support:
    
    	* Added liboauth's sources to our tree
    	* Added couchdb_enable_oauth/couchdb_disable_oauth API
    	* Added OAuth signature code to all requests when OAuth is enabled
    	* Added OAuth test programs

 configure.ac                |   23 +
 couchdb-glib/Makefile.am    |   14 +-
 couchdb-glib/couchdb-glib.h |    7 +
 couchdb-glib/couchdb.c      |   48 +++
 couchdb-glib/oauth.c        |  961 +++++++++++++++++++++++++++++++++++++++++++
 couchdb-glib/oauth.h        |  539 ++++++++++++++++++++++++
 couchdb-glib/utils.c        |   88 ++++-
 couchdb-glib/utils.h        |    9 +
 couchdb-glib/xmalloc.c      |  151 +++++++
 couchdb-glib/xmalloc.h      |   12 +
 tests/Makefile.am           |   14 +-
 tests/test-oauth.c          |  134 ++++++
 tests/test-oauth.py         |   32 ++
 13 files changed, 2027 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b00bfca..1d30b02 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,6 +18,19 @@ AC_PROG_LIBTOOL
 dnl glib-genmarshal
 AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
 
+dnl Check for oAuth dependencies
+have_oauth="no"
+AC_CHECK_HEADERS(unistd.h time.h string.h alloca.h stdio.h stdarg.h math.h openssl/hmac.h, have_oauth_headers="yes", have_oauth_headers="no")
+AC_CHECK_LIB(crypto, CRYPTO_free, have_oauth_libs="yes", have_oauth_libs="no")
+if test "x$have_oauth_headers" = "xyes" -a "x$have_oauth_libs" = "xyes"; then
+   AC_DEFINE(HAVE_OAUTH, 1, [Define to build OAUTH signing code])
+   AC_DEFINE(DEBUG_OAUTH, 1, [Define to enable OAuth debugging])
+   have_oauth="yes"
+   OAUTH_LIBS="-lm -lcrypto"
+   AC_SUBST(OAUTH_LIBS)
+fi
+AM_CONDITIONAL(HAVE_OAUTH, test "x$have_oauth" = "xyes")
+
 dnl Look for needed modules
 PKG_CHECK_MODULES(COUCHDB_GLIB, glib-2.0 gobject-2.0 json-glib-1.0 >= 0.7.4 libsoup-2.4 libsoup-gnome-2.4 uuid)
 AC_SUBST(COUCHDB_GLIB_CFLAGS)
@@ -36,3 +49,13 @@ couchdb-glib.pc
 couchdb-glib/Makefile
 tests/Makefile
 ])
+
+AC_MSG_NOTICE([
+
+ couchdb-glib configured:
+ ------------------------
+
+ version: $VERSION
+ oAuth:   $have_oauth
+
+])
diff --git a/couchdb-glib/Makefile.am b/couchdb-glib/Makefile.am
index 8fc2209..0512d57 100644
--- a/couchdb-glib/Makefile.am
+++ b/couchdb-glib/Makefile.am
@@ -1,3 +1,13 @@
+OAUTH_FILES = oauth.c oauth.h xmalloc.c xmalloc.h
+
+if HAVE_OAUTH
+OAUTH_SOURCES = $(OAUTH_FILES)
+OAUTH_LIBS = 
+else
+OAUTH_SOURCES = 
+OAUTH_LIBS =
+endif
+
 INCLUDES =		\
 	$(COUCHDB_GLIB_CFLAGS)
 
@@ -20,10 +30,12 @@ libcouchdb_glib_1_0_la_SOURCES =	\
 	couchdb-types.c			\
 	dbwatch.c			\
 	dbwatch.h			\
+	$(OAUTH_SOURCES)		\
 	utils.c				\
 	utils.h
 libcouchdb_glib_1_0_la_LIBADD =		\
 	$(COUCHDB_GLIB_LIBS)		\
+	$(OAUTH_LIBS)			\
 	-luuid
 libcouchdb_glib_1_0_la_LDFLAGS =	\
 	-version-info $(LIBCOUCHDBGLIB_CURRENT):$(LIBCOUCHDBGLIB_REVISION):$(LIBCOUCHDBGLIB_AGE)
@@ -34,6 +46,6 @@ h_DATA = 				\
 	couchdb-document-contact.h	\
 	couchdb-types.h
 
-EXTRA_DIST = $(h_DATA) $(MARSHAL_GENERATED)
+EXTRA_DIST = $(h_DATA) $(MARSHAL_GENERATED) $(OAUTH_FILES)
 BUILT_SOURCES = $(MARSHAL_GENERATED)
 CLEANFILES = $(BUILT_SOURCES)
diff --git a/couchdb-glib/couchdb-glib.h b/couchdb-glib/couchdb-glib.h
index 43a84c5..5d1ad06 100644
--- a/couchdb-glib/couchdb-glib.h
+++ b/couchdb-glib/couchdb-glib.h
@@ -64,6 +64,13 @@ gboolean             couchdb_delete_database (CouchDB *couchdb, const char *dbna
 
 void                 couchdb_listen_for_changes (CouchDB *couchdb, const char *dbname);
 
+gboolean             couchdb_enable_oauth (CouchDB *couchdb,
+					   const char *consumer_key,
+					   const char *consumer_secret,
+					   const char *token_key,
+					   const char *token_secret);
+void                 couchdb_disable_oauth (CouchDB *couchdb);
+
 /*
  * Documents API
  */
diff --git a/couchdb-glib/couchdb.c b/couchdb-glib/couchdb.c
index db69c22..d5a0367 100644
--- a/couchdb-glib/couchdb.c
+++ b/couchdb-glib/couchdb.c
@@ -49,6 +49,13 @@ couchdb_finalize (GObject *object)
 	g_free (couchdb->hostname);
 	g_object_unref (couchdb->http_session);
 
+#ifdef HAVE_OAUTH
+	g_free (couchdb->oauth_consumer_key);
+	g_free (couchdb->oauth_consumer_secret);
+	g_free (couchdb->oauth_token_key);
+	g_free (couchdb->oauth_token_secret);
+#endif
+
 	G_OBJECT_CLASS (couchdb_parent_class)->finalize (object);
 }
 
@@ -375,3 +382,44 @@ couchdb_listen_for_changes (CouchDB *couchdb, const char *dbname)
 	/* Free memory */
 	couchdb_database_info_unref (db_info);
 }
+
+/**
+ * couchdb_enable_oauth:
+ * @couchdb: A #CouchDB object
+ * @consumer_key: Consumer key to use
+ * @consumer_secret: Consumer secret to use
+ * @token_key: Token key to use
+ * @token_secret: Token secret to use
+ *
+ * Enables oAuth signing of all requests for the given #CouchDB object.
+ *
+ * Return value: TRUE if oAuth is enabled in the library or FALSE if not.
+ */
+gboolean
+couchdb_enable_oauth (CouchDB *couchdb,
+		      const char *consumer_key,
+		      const char *consumer_secret,
+		      const char *token_key,
+		      const char *token_secret)
+{
+	g_return_val_if_fail (COUCHDB_IS (couchdb), FALSE);
+
+#ifdef HAVE_OAUTH
+	if (couchdb->oauth_enabled) {
+		g_free (couchdb->oauth_consumer_key);
+		g_free (couchdb->oauth_consumer_secret);
+		g_free (couchdb->oauth_token_key);
+		g_free (couchdb->oauth_token_secret);
+	}
+
+	couchdb->oauth_consumer_key = g_strdup (consumer_key);
+	couchdb->oauth_consumer_secret = g_strdup (consumer_secret);
+	couchdb->oauth_token_key = g_strdup (token_key);
+	couchdb->oauth_token_secret = g_strdup (token_secret);
+	couchdb->oauth_enabled = TRUE;
+
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
diff --git a/couchdb-glib/oauth.c b/couchdb-glib/oauth.c
new file mode 100644
index 0000000..3b5ee05
--- /dev/null
+++ b/couchdb-glib/oauth.c
@@ -0,0 +1,961 @@
+/*
+ * oAuth string functions in POSIX-C.
+ *
+ * Copyright 2007, 2008, 2009 Robin Gareus <robin gareus org>
+ * 
+ * The base64 functions are by Jan-Henrik Haukeland, <hauk tildeslash com>
+ * and un/escape_url() was inspired by libcurl's curl_escape under ISC-license
+ * many thanks to Daniel Stenberg <daniel haxx se>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ * 
+ */
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define WIPE_MEMORY ///< overwrite sensitve data before free()ing it.
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <ctype.h> // isxdigit
+#include <openssl/hmac.h>
+
+#include "xmalloc.h"
+#include "oauth.h"
+
+#ifndef WIN // getpid() on POSIX systems
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+/**
+ * Base64 encode one byte
+ */
+char oauth_b64_encode(unsigned char u) {
+  if(u < 26)  return 'A'+u;
+  if(u < 52)  return 'a'+(u-26);
+  if(u < 62)  return '0'+(u-52);
+  if(u == 62) return '+';
+  return '/';
+}
+
+/**
+ * Decode a single base64 character.
+ */
+unsigned char oauth_b64_decode(char c) {
+  if(c >= 'A' && c <= 'Z') return(c - 'A');
+  if(c >= 'a' && c <= 'z') return(c - 'a' + 26);
+  if(c >= '0' && c <= '9') return(c - '0' + 52);
+  if(c == '+')             return 62;
+  return 63;
+}
+
+/**
+ * Return TRUE if 'c' is a valid base64 character, otherwise FALSE
+ */
+int oauth_b64_is_base64(char c) {
+  if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+     (c >= '0' && c <= '9') || (c == '+')             ||
+     (c == '/')             || (c == '=')) {
+    return 1;
+  }
+  return 0;
+}
+
+/**
+ * Base64 encode and return size data in 'src'. The caller must free the
+ * returned string.
+ *
+ * @param size The size of the data in src
+ * @param src The data to be base64 encode
+ * @return encoded string otherwise NULL
+ */
+char *oauth_encode_base64(int size, const unsigned char *src) {
+  int i;
+  char *out, *p;
+
+  if(!src) return NULL;
+  if(!size) size= strlen((char *)src);
+  out= (char*) xcalloc(sizeof(char), size*4/3+4);
+  p= out;
+
+  for(i=0; i<size; i+=3) {
+    unsigned char b1=0, b2=0, b3=0, b4=0, b5=0, b6=0, b7=0;
+    b1= src[i];
+    if(i+1<size) b2= src[i+1];
+    if(i+2<size) b3= src[i+2];
+      
+    b4= b1>>2;
+    b5= ((b1&0x3)<<4)|(b2>>4);
+    b6= ((b2&0xf)<<2)|(b3>>6);
+    b7= b3&0x3f;
+      
+    *p++= oauth_b64_encode(b4);
+    *p++= oauth_b64_encode(b5);
+      
+    if(i+1<size) *p++= oauth_b64_encode(b6);
+    else *p++= '=';
+      
+    if(i+2<size) *p++= oauth_b64_encode(b7);
+    else *p++= '=';
+  }
+  return out;
+}
+
+/**
+ * Decode the base64 encoded string 'src' into the memory pointed to by
+ * 'dest'. 
+ *
+ * @param dest Pointer to memory for holding the decoded string.
+ * Must be large enough to recieve the decoded string.
+ * @param src A base64 encoded string.
+ * @return the length of the decoded string if decode
+ * succeeded otherwise 0.
+ */
+int oauth_decode_base64(unsigned char *dest, const char *src) {
+  if(src && *src) {
+    unsigned char *p= dest;
+    int k, l= strlen(src)+1;
+    unsigned char *buf= (unsigned char*) xcalloc(sizeof(unsigned char), l);
+
+    /* Ignore non base64 chars as per the POSIX standard */
+    for(k=0, l=0; src[k]; k++) {
+      if(oauth_b64_is_base64(src[k])) {
+        buf[l++]= src[k];
+      }
+    } 
+    
+    for(k=0; k<l; k+=4) {
+      char c1='A', c2='A', c3='A', c4='A';
+      unsigned char b1=0, b2=0, b3=0, b4=0;
+      c1= buf[k];
+
+      if(k+1<l) c2= buf[k+1];
+      if(k+2<l) c3= buf[k+2];
+      if(k+3<l) c4= buf[k+3];
+      
+      b1= oauth_b64_decode(c1);
+      b2= oauth_b64_decode(c2);
+      b3= oauth_b64_decode(c3);
+      b4= oauth_b64_decode(c4);
+      
+      *p++=((b1<<2)|(b2>>4) );
+      
+      if(c3 != '=') *p++=(((b2&0xf)<<4)|(b3>>2) );
+      if(c4 != '=') *p++=(((b3&0x3)<<6)|b4 );
+    }
+    free(buf);
+    dest[p-dest]='\0';
+    return(p-dest);
+  }
+  return 0;
+}
+
+/**
+ * Escape 'string' according to RFC3986 and
+ * http://oauth.net/core/1.0/#encoding_parameters.
+ *
+ * @param string The data to be encoded
+ * @return encoded string otherwise NULL
+ * The caller must free the returned string.
+ */
+char *oauth_url_escape(const char *string) {
+  size_t alloc, newlen;
+  char *ns = NULL, *testing_ptr = NULL;
+  unsigned char in; 
+  size_t strindex=0;
+  size_t length;
+
+  if (!string) return xstrdup("");
+
+  alloc = strlen(string)+1;
+  newlen = alloc;
+
+  ns = (char*) xmalloc(alloc);
+
+  length = alloc-1;
+  while(length--) {
+    in = *string;
+
+    switch(in){
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+    case 'a': case 'b': case 'c': case 'd': case 'e':
+    case 'f': case 'g': case 'h': case 'i': case 'j':
+    case 'k': case 'l': case 'm': case 'n': case 'o':
+    case 'p': case 'q': case 'r': case 's': case 't':
+    case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+    case 'A': case 'B': case 'C': case 'D': case 'E':
+    case 'F': case 'G': case 'H': case 'I': case 'J':
+    case 'K': case 'L': case 'M': case 'N': case 'O':
+    case 'P': case 'Q': case 'R': case 'S': case 'T':
+    case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
+    case '_': case '~': case '.': case '-':
+      ns[strindex++]=in;
+      break;
+    default:
+      newlen += 2; /* this'll become a %XX */
+      if(newlen > alloc) {
+        alloc *= 2;
+				testing_ptr = (char*) xrealloc(ns, alloc);
+				ns = testing_ptr;
+      }
+      snprintf(&ns[strindex], 4, "%%%02X", in);
+      strindex+=3;
+      break;
+    }
+    string++;
+  }
+  ns[strindex]=0;
+  return ns;
+}
+
+#ifndef ISXDIGIT
+# define ISXDIGIT(x) (isxdigit((int) ((unsigned char)x)))
+#endif
+
+/**
+ * Parse RFC3986 encoded 'string' back to  unescaped version.
+ *
+ * @param string The data to be unescaped
+ * @param olen unless NULL the length of the returned string is stored there.
+ * @return decoded string or NULL
+ * The caller must free the returned string.
+ */
+char *oauth_url_unescape(const char *string, size_t *olen) {
+  size_t alloc, strindex=0;
+  char *ns = NULL;
+  unsigned char in;
+  long hex;
+
+	if (!string) return NULL;
+  alloc = strlen(string)+1;
+  ns = (char*) xmalloc(alloc);
+
+  while(--alloc > 0) {
+    in = *string;
+    if(('%' == in) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
+      char hexstr[3]; // '%XX'
+      hexstr[0] = string[1];
+      hexstr[1] = string[2];
+      hexstr[2] = 0;
+      hex = strtol(hexstr, NULL, 16);
+      in = (unsigned char)hex; /* hex is always < 256 */
+      string+=2;
+      alloc-=2;
+    }
+    ns[strindex++] = in;
+    string++;
+  }
+  ns[strindex]=0;
+  if(olen) *olen = strindex;
+  return ns;
+}
+
+/**
+ * returns base64 encoded HMAC-SHA1 signature for
+ * given message and key.
+ * both data and key need to be urlencoded.
+ *
+ * the returned string needs to be freed by the caller
+ *
+ * @param m message to be signed
+ * @param k key used for signing
+ * @return signature string.
+ */
+char *oauth_sign_hmac_sha1 (const char *m, const char *k) {
+  return(oauth_sign_hmac_sha1_raw (m, strlen(m), k, strlen(k)));
+}
+
+char *oauth_sign_hmac_sha1_raw (const char *m, const size_t ml, const char *k, const size_t kl) {
+  unsigned char result[EVP_MAX_MD_SIZE];
+  unsigned int resultlen = 0;
+  
+  HMAC(EVP_sha1(), k, kl, 
+      (unsigned char*) m, ml,
+      result, &resultlen);
+
+  return(oauth_encode_base64(resultlen, result));
+}
+
+/**
+ * returns plaintext signature for the given key.
+ *
+ * the returned string needs to be freed by the caller
+ *
+ * @param m message to be signed
+ * @param k key used for signing
+ * @return signature string
+ */
+char *oauth_sign_plaintext (const char *m, const char *k) {
+  return(oauth_url_escape(k));
+}
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/ssl.h>
+
+/**
+ * returns RSA-SHA1 signature for given data.
+ * the returned signature needs to be freed by the caller.
+ *
+ * @param m message to be signed
+ * @param k private-key PKCS and Base64-encoded 
+ * @return base64 encoded signature string.
+ */
+char *oauth_sign_rsa_sha1 (const char *m, const char *k) {
+  unsigned char *sig = NULL;
+  unsigned char *passphrase = NULL;
+  unsigned int len=0;
+  EVP_MD_CTX md_ctx;
+
+  EVP_PKEY *pkey;
+  BIO *in;
+  in = BIO_new_mem_buf((unsigned char*) k, strlen(k));
+  pkey = PEM_read_bio_PrivateKey(in, NULL, 0, passphrase); // generate sign
+  BIO_free(in);
+
+  if (pkey == NULL) {
+  //fprintf(stderr, "liboauth/ssl: can not read private key\n");
+	  return xstrdup("liboauth/ssl: can not read private key");
+  }
+
+  len = EVP_PKEY_size(pkey);
+  sig = xmalloc((len+1)*sizeof(char));
+
+  EVP_SignInit(&md_ctx, EVP_sha1());
+  EVP_SignUpdate(&md_ctx, m, strlen(m));
+  if (EVP_SignFinal (&md_ctx, sig, &len, pkey)) {
+	  char *tmp;
+    sig[len] = '\0';
+		tmp = oauth_encode_base64(len,sig);
+		OPENSSL_free(sig);
+	  EVP_PKEY_free(pkey);
+		return tmp;
+  }
+  return xstrdup("liboauth/ssl: rsa-sha1 signing failed");
+}
+
+/**
+ * verify RSA-SHA1 signature.
+ *
+ * returns the output of EVP_VerifyFinal() for a given message,
+ * cert/pubkey and signature
+ *
+ * @param m message to be verified
+ * @param c public-key or x509 certificate
+ * @param s base64 encoded signature
+ * @return 1 for a correct signature, 0 for failure and -1 if some other error occurred
+ */
+int oauth_verify_rsa_sha1 (const char *m, const char *c, const char *s) {
+  EVP_MD_CTX md_ctx;
+  EVP_PKEY *pkey;
+  BIO *in;
+  X509 *cert = NULL;
+	unsigned char *b64d;
+  int slen, err;
+
+  in = BIO_new_mem_buf((unsigned char*)c, strlen(c));
+  cert = PEM_read_bio_X509(in, NULL, 0, NULL);
+	if (cert)  {
+    pkey = (EVP_PKEY *) X509_get_pubkey(cert); 
+		X509_free(cert);
+	} else {
+    pkey = PEM_read_bio_PUBKEY(in, NULL, 0, NULL);
+	}
+  BIO_free(in);
+  if (pkey == NULL) {
+  //fprintf(stderr, "could not read cert/pubkey.\n");
+	  return -2;
+  }
+
+  b64d= (unsigned char*) xmalloc(sizeof(char)*strlen(s));
+  slen = oauth_decode_base64(b64d, s);
+
+	EVP_VerifyInit(&md_ctx, EVP_sha1());
+	EVP_VerifyUpdate(&md_ctx, m, strlen(m));
+	err = EVP_VerifyFinal(&md_ctx, b64d, slen, pkey);
+	EVP_MD_CTX_cleanup(&md_ctx);
+	EVP_PKEY_free(pkey);
+	free(b64d);
+	return (err);
+}
+
+/**
+ * encode strings and concatenate with '&' separator.
+ * The number of strings to be concatenated must be
+ * given as first argument.
+ * all arguments thereafter must be of type (char *) 
+ *
+ * @param len the number of arguments to follow this parameter
+ * @param ... string to escape and added (may be NULL)
+ *
+ * @return pointer to memory holding the concatenated 
+ * strings - needs to be free(d) by the caller. or NULL
+ * in case we ran out of memory.
+ */
+char *oauth_catenc(int len, ...) {
+  va_list va;
+  int i;
+  char *rv = (char*) xmalloc(sizeof(char));
+  *rv='\0';
+  va_start(va, len);
+  for(i=0;i<len;i++) {
+    char *arg = va_arg(va, char *);
+    char *enc;
+    int len;
+    enc = oauth_url_escape(arg);
+    if(!enc) break;
+    len = strlen(enc) + 1 + ((i>0)?1:0);
+    if(rv) len+=strlen(rv);
+    rv=(char*) xrealloc(rv,len*sizeof(char));
+
+    if(i>0) strcat(rv, "&");
+    strcat(rv, enc);
+    free(enc);
+  }
+  va_end(va);
+  return(rv);
+}
+
+/**
+ * splits the given url into a parameter array. 
+ * (see \ref oauth_serialize_url and \ref oauth_serialize_url_parameters for the reverse)
+ *
+ * NOTE: Request-parameters-values may include an ampersand character.
+ * However if unescaped this function will use them as parameter delimiter. 
+ * If you need to make such a request, this function since version 0.3.5 allows
+ * to use the ASCII SOH (0x01) character as alias for '&' (0x26).
+ * (the motivation is convenience: SOH is /untypeable/ and much more 
+ * unlikely to appear than '&' - If you plan to sign fancy URLs you 
+ * should not split a query-string, but rather provide the parameter array
+ * directly to \ref oauth_serialize_url)
+ *
+ * @param url the url or query-string to parse. 
+ * @param argv pointer to a (char *) array where the results are stored.
+ *  The array is re-allocated to match the number of parameters and each 
+ *  parameter-string is allocated with strdup. - The memory needs to be freed
+ *  by the caller.
+ * @param qesc use query parameter escape (vs post-param-escape) - if set
+ *        to 1 all '+' are treated as spaces ' '
+ * 
+ * @return number of parameter(s) in array.
+ */
+int oauth_split_post_paramters(const char *url, char ***argv, short qesc) {
+  int argc=0;
+  char *token, *tmp, *t1;
+  if (!argv) return 0;
+  if (!url) return 0;
+  t1=xstrdup(url);
+
+  // '+' represents a space, in a URL query string
+  while ((qesc&1) && (tmp=strchr(t1,'+'))) *tmp=' ';
+
+  tmp=t1;
+  while((token=strtok(tmp,"&?"))) {
+    if(!strncasecmp("oauth_signature=",token,16)) continue;
+    (*argv)=(char**) xrealloc(*argv,sizeof(char*)*(argc+1));
+    while (!(qesc&2) && (tmp=strchr(token,'\001'))) *tmp='&';
+		(*argv)[argc]=oauth_url_unescape(token, NULL);
+	  if (argc==0 && strstr(token, ":/")) {
+			// HTTP does not allow empty absolute paths, so the URL 
+			// 'http://example.com' is equivalent to 'http://example.com/' and should
+			// be treated as such for the purposes of OAuth signing (rfc2616, section 3.2.1)
+			// see http://groups.google.com/group/oauth/browse_thread/thread/c44b6f061bfd98c?hl=en
+			char *slash=strstr(token, ":/");
+			while (slash && *(++slash) == '/')  ; // skip slashes eg /xxx:[\/]*/
+#if 0
+			// skip possibly unescaped slashes in the userinfo - they're not allowed by RFC2396 but have been seen.
+			// the hostname/IP may only contain alphanumeric characters - so we're safe there.
+			if (slash && strchr(slash,'@')) slash=strchr(slash,'@'); 
+#endif
+			if (slash && !strchr(slash,'/')) {
+#ifdef DEBUG_OAUTH
+			  fprintf(stderr, "\nliboauth: added trailing slash to URL: '%s'\n\n", token);
+#endif
+				free((*argv)[argc]);
+				(*argv)[argc]= (char*) xmalloc(sizeof(char)*(2+strlen(token))); 
+				strcpy((*argv)[argc],token);
+				strcat((*argv)[argc],"/");
+		  }
+		}
+	  if (argc==0 && (tmp=strstr((*argv)[argc],":80/"))) {
+			  memmove(tmp, tmp+3, strlen(tmp+2));
+		}
+    tmp=NULL;
+    argc++;
+  }
+
+  free(t1);
+  return argc;
+}
+
+int oauth_split_url_parameters(const char *url, char ***argv) {
+  return oauth_split_post_paramters(url, argv, 1);
+}
+
+/**
+ * build a url query string from an array.
+ *
+ * @param argc the total number of elements in the array
+ * @param start element in the array at which to start concatenating.
+ * @param argv parameter-array to concatenate.
+ * @return url string needs to be freed by the caller.
+ *
+ */
+char *oauth_serialize_url (int argc, int start, char **argv) {
+	return oauth_serialize_url_sep( argc, start, argv, "&");
+}
+
+/**
+ * encode query parameters from an array.
+ *
+ * @param argc the total number of elements in the array
+ * @param start element in the array at which to start concatenating.
+ * @param argv parameter-array to concatenate.
+ * @param sep separator for parameters (usually "&") 
+ * @return url string needs to be freed by the caller.
+ */
+char *oauth_serialize_url_sep (int argc, int start, char **argv, char *sep) {
+  char  *tmp, *t1;
+  int i;
+  int	first=0;
+	int seplen=strlen(sep);
+  char *query = (char*) xmalloc(sizeof(char)); 
+  *query='\0';
+  for(i=start; i< argc; i++) {
+    int len = 0;
+    if (query) len+=strlen(query);
+
+		if (i==start && i==0 && strstr(argv[i], ":/")) {
+      tmp=xstrdup(argv[i]);
+      len+=strlen(tmp);
+		} else if(!(t1=strchr(argv[i], '='))) {
+    // see http://oauth.net/core/1.0/#anchor14
+    // escape parameter names and arguments but not the '='
+      tmp=xstrdup(argv[i]);
+      tmp=(char*) xrealloc(tmp,(strlen(tmp)+2)*sizeof(char));
+      strcat(tmp,"=");
+      len+=strlen(tmp);
+    } else {
+      *t1=0;
+      tmp = oauth_url_escape(argv[i]);
+      *t1='=';
+      t1 = oauth_url_escape((t1+1));
+      tmp=(char*) xrealloc(tmp,(strlen(tmp)+strlen(t1)+2)*sizeof(char));
+      strcat(tmp,"=");
+      strcat(tmp,t1);
+      free(t1);
+      len+=strlen(tmp);
+    }
+		len+=seplen+1;
+    query=(char*) xrealloc(query,len*sizeof(char));
+    strcat(query, ((i==start||first)?"":sep));
+		first=0;
+    strcat(query, tmp);
+		if (i==start && i==0 && strstr(tmp, ":/")) {
+			strcat(query, "?");
+			first=1;
+		}
+    free(tmp);
+  }
+  return (query);
+}
+
+/**
+ * build a query parameter string from an array.
+ *
+ * This function is a shortcut for \ref oauth_serialize_url (argc, 1, argv). 
+ * It strips the leading host/path, which is usually the first 
+ * element when using oauth_split_url_parameters on an URL.
+ *
+ * @param argc the total number of elements in the array
+ * @param argv parameter-array to concatenate.
+ * @return url string needs to be freed by the caller.
+ */
+char *oauth_serialize_url_parameters (int argc, char **argv) {
+  return oauth_serialize_url(argc, 1, argv);
+}
+
+/**
+ * generate a random string between 15 and 32 chars length
+ * and return a pointer to it. The value needs to be freed by the
+ * caller
+ *
+ * @return zero terminated random string.
+ */
+char *oauth_gen_nonce() {
+  char *nc;
+  static int rndinit = 1;
+  const char *chars = "abcdefghijklmnopqrstuvwxyz"
+  	"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_";
+  unsigned int max = strlen( chars );
+  int i, len;
+
+  if(rndinit) {srand(time(NULL) 
+#ifndef WIN // quick windows check.
+  	* getpid()
+#endif
+	); rndinit=0;} // seed random number generator - FIXME: we can do better ;)
+
+  len=15+floor(rand()*16.0/(double)RAND_MAX);
+  nc = (char*) xmalloc((len+1)*sizeof(char));
+  for(i=0;i<len; i++) {
+    nc[i] = chars[ rand() % max ];
+  }
+  nc[i]='\0';
+  return (nc);
+}
+
+/**
+ * string compare function for oauth parameters.
+ *
+ * used with qsort. needed to normalize request parameters.
+ * see http://oauth.net/core/1.0/#anchor14
+ */
+int oauth_cmpstringp(const void *p1, const void *p2) {
+  char *v1,*v2;
+  char *t1,*t2;
+  int rv;
+  // TODO: this is not fast - we should escape the 
+  // array elements (once) before sorting.
+  v1=oauth_url_escape(* (char * const *)p1);
+  v2=oauth_url_escape(* (char * const *)p2);
+
+  // '=' signs are not "%3D" !
+  if ((t1=strstr(v1,"%3D"))) {
+    t1[0]='\0'; t1[1]='='; t1[2]='=';
+  }
+  if ((t2=strstr(v2,"%3D"))) {
+    t2[0]='\0'; t2[1]='='; t2[2]='=';
+  }
+
+  // compare parameter names
+  rv=strcmp(v1,v2);
+  if (rv!=0) {
+    if (v1) free(v1);
+    if (v2) free(v2);
+    return rv;
+  }
+
+  // if parameter names are equal, sort by value.
+  if (t1) t1[0]='='; 
+  if (t2) t2[0]='='; 
+  rv=strcmp(t1,t2);
+  if (v1) free(v1);
+  if (v2) free(v2);
+  return rv;
+}
+
+/**
+ * search array for parameter key.
+ * @param argv length of array to search
+ * @param argc parameter array to search
+ * @param key key of parameter to check.
+ *
+ * @return FALSE (0) if array does not contain a paramater with given key, TRUE (1) otherwise.
+ */
+int oauth_param_exists(char **argv, int argc, char *key) {
+	int i;
+	size_t l= strlen(key);
+	for (i=0;i<argc;i++)
+		if (strlen(argv[i])>l && !strncmp(argv[i],key,l) && argv[i][l] == '=') return 1;
+	return 0;
+}
+
+/**
+ * add query parameter to array
+ *
+ * @param argcp pointer to array length int
+ * @param argvp pointer to array values 
+ * @param addparam parameter to add (eg. "foo=bar")
+ */
+void oauth_add_param_to_array(int *argcp, char ***argvp, const char *addparam) {
+  (*argvp)=(char**) xrealloc(*argvp,sizeof(char*)*((*argcp)+1));
+  (*argvp)[(*argcp)++]= (char*) xstrdup(addparam);
+}
+
+/**
+ *
+ */
+void oauth_add_protocol(int *argcp, char ***argvp, 
+  OAuthMethod method, 
+  const char *c_key, //< consumer key - posted plain text
+  const char *t_key //< token key - posted plain text in URL
+ ){
+  char oarg[1024];
+
+  // add oAuth specific arguments
+	if (!oauth_param_exists(*argvp,*argcp,"oauth_nonce")) {
+		char *tmp;
+		snprintf(oarg, 1024, "oauth_nonce=%s", (tmp=oauth_gen_nonce()));
+		oauth_add_param_to_array(argcp, argvp, oarg);
+		free(tmp);
+	}
+
+	if (!oauth_param_exists(*argvp,*argcp,"oauth_timestamp")) {
+		snprintf(oarg, 1024, "oauth_timestamp=%li", (long int) time(NULL));
+		oauth_add_param_to_array(argcp, argvp, oarg);
+	}
+
+	if (t_key) {
+    snprintf(oarg, 1024, "oauth_token=%s", t_key);
+		oauth_add_param_to_array(argcp, argvp, oarg);
+  }
+
+  snprintf(oarg, 1024, "oauth_consumer_key=%s", c_key);
+	oauth_add_param_to_array(argcp, argvp, oarg);
+
+  snprintf(oarg, 1024, "oauth_signature_method=%s",
+      method==0?"HMAC-SHA1":method==1?"RSA-SHA1":"PLAINTEXT");
+	oauth_add_param_to_array(argcp, argvp, oarg);
+
+	if (!oauth_param_exists(*argvp,*argcp,"oauth_version")) {
+		snprintf(oarg, 1024, "oauth_version=1.0");
+		oauth_add_param_to_array(argcp, argvp, oarg);
+	}
+
+	strcpy(oarg, "oauth_verifier=None");
+	oauth_add_param_to_array(argcp, argvp, oarg);
+
+	strcpy(oarg, "oauth_callback=None");
+	oauth_add_param_to_array(argcp, argvp, oarg);
+
+#if 0 // oauth_version 1.0 Rev A
+	if (!oauth_param_exists(argv,argc,"oauth_callback")) {
+		snprintf(oarg, 1024, "oauth_callback=oob");
+		oauth_add_param_to_array(argcp, argvp, oarg);
+	}
+#endif
+
+}
+
+char *oauth_sign_url (const char *url, char **postargs, 
+  OAuthMethod method, 
+  const char *c_key, //< consumer key - posted plain text
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_key, //< token key - posted plain text in URL
+  const char *t_secret //< token secret - used as 2st part of secret-key
+  ) {
+  return oauth_sign_url2(url, postargs, 
+    method, NULL,
+		c_key, c_secret,
+		t_key, t_secret);
+} 
+
+char *oauth_sign_url2 (const char *url, char **postargs, 
+  OAuthMethod method, 
+  const char *http_method, //< HTTP request method
+  const char *c_key, //< consumer key - posted plain text
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_key, //< token key - posted plain text in URL
+  const char *t_secret //< token secret - used as 2st part of secret-key
+  ) {
+  int  argc;
+  char **argv = NULL;
+  char *rv;
+
+  if (postargs)
+    argc = oauth_split_post_paramters(url, &argv, 0);
+  else
+    argc = oauth_split_url_parameters(url, &argv);
+
+  rv=oauth_sign_array2(&argc, &argv, postargs, 
+		method, http_method,
+    c_key, c_secret, t_key, t_secret);
+
+  oauth_free_array(&argc, &argv);
+	return(rv);
+}
+
+char *oauth_sign_array (int *argcp, char***argvp,
+  char **postargs,
+  OAuthMethod method, 
+  const char *c_key, //< consumer key - posted plain text
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_key, //< token key - posted plain text in URL
+  const char *t_secret //< token secret - used as 2st part of secret-key
+  ) {
+  return oauth_sign_array2 (argcp, argvp, 
+                            postargs, method,
+                            NULL,
+                            c_key, c_secret,
+                            t_key, t_secret);
+}
+
+char *oauth_sign_array2 (int *argcp, char***argvp,
+  char **postargs,
+  OAuthMethod method, 
+  const char *http_method, //< HTTP request method
+  const char *c_key, //< consumer key - posted plain text
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_key, //< token key - posted plain text in URL
+  const char *t_secret //< token secret - used as 2st part of secret-key
+  ) {
+  char oarg[1024];
+  char *query;
+  char *okey, *odat, *sign;
+  char *result;
+  char *http_request_method;
+
+  if (!http_method) {
+    http_request_method = xstrdup(postargs?"POST":"GET");
+  } else {
+    int i;
+    http_request_method = xstrdup(http_method);
+    for (i=0;i<strlen(http_request_method);i++) 
+      http_request_method[i]=toupper(http_request_method[i]);
+  }
+
+	// add OAuth protocol parameters
+	oauth_add_protocol(argcp, argvp, method, c_key, t_key);
+
+  // sort parameters
+  qsort(&(*argvp)[1], (*argcp)-1, sizeof(char *), oauth_cmpstringp);
+
+  // serialize URL
+  query= oauth_serialize_url_parameters(*argcp, *argvp);
+
+  // generate signature
+  okey = oauth_catenc(2, c_secret, t_secret);
+  odat = oauth_catenc(3, http_request_method, (*argvp)[0], query);
+  free(http_request_method);
+#ifdef DEBUG_OAUTH
+  fprintf (stderr, "\nliboauth: data to sign='%s'\n\n", odat);
+  fprintf (stderr, "\nliboauth: key='%s'\n\n", okey);
+#endif
+  switch(method) {
+    case OA_RSA:
+      sign = oauth_sign_rsa_sha1(odat,okey); // XXX okey needs to be RSA key!
+    	break;
+    case OA_PLAINTEXT:
+      sign = oauth_sign_plaintext(odat,okey);
+    	break;
+    default:
+      sign = oauth_sign_hmac_sha1(odat,okey);
+  }
+#ifdef WIPE_MEMORY
+	memset(okey,0, strlen(okey));
+	memset(odat,0, strlen(odat));
+#endif
+  free(odat); 
+  free(okey);
+
+  // append signature to query args.
+  snprintf(oarg, 1024, "oauth_signature=%s",sign);
+	oauth_add_param_to_array(argcp, argvp, oarg);
+  free(sign);
+
+  // build URL params
+  result = oauth_serialize_url(*argcp, (postargs?1:0), *argvp);
+
+  if(postargs) { 
+    *postargs = result;
+    result = xstrdup((*argvp)[0]);
+  }
+
+  if(query) free(query);
+
+  return result;
+}
+
+/**
+ * free array args
+ *
+ * @param argcp pointer to array length int
+ * @param argvp pointer to array values to be free()d
+ */
+void oauth_free_array(int *argcp, char ***argvp) {
+  int i;
+	for (i=0;i<(*argcp);i++) {
+		free((*argvp)[i]);
+	}
+  if(*argvp) free(*argvp);
+}
+
+/** 
+ * http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html
+ */
+char *oauth_body_hash_file(char *filename) {
+  unsigned char fb[BUFSIZ];
+  EVP_MD_CTX ctx;
+  size_t len=0;
+  unsigned char *md;
+  FILE *F= fopen(filename, "r");
+  if (!F) return NULL;
+
+  EVP_MD_CTX_init(&ctx);
+  EVP_DigestInit(&ctx,EVP_sha1());
+  while (!feof(F) && (len=fread(fb,sizeof(char),BUFSIZ, F))>0) {
+    EVP_DigestUpdate(&ctx, fb, len);
+  }
+  fclose(F);
+  len=0;
+  md=(unsigned char*) calloc(EVP_MD_size(EVP_sha1()),sizeof(unsigned char));
+  EVP_DigestFinal(&ctx, md, &len);
+  EVP_MD_CTX_cleanup(&ctx);
+  return oauth_body_hash_encode(len, md);
+}
+
+char *oauth_body_hash_data(size_t length, const char *data) {
+  EVP_MD_CTX ctx;
+  size_t len=0;
+  unsigned char *md;
+  md=(unsigned char*) calloc(EVP_MD_size(EVP_sha1()),sizeof(unsigned char));
+  EVP_MD_CTX_init(&ctx);
+  EVP_DigestInit(&ctx,EVP_sha1());
+  EVP_DigestUpdate(&ctx, data, length);
+  EVP_DigestFinal(&ctx, md, &len);
+  EVP_MD_CTX_cleanup(&ctx);
+  return oauth_body_hash_encode(len, md);
+}
+
+/**
+ * base64 encode digest, free it and return a URL parameter
+ * with the oauth_body_hash
+ */
+char *oauth_body_hash_encode(size_t len, unsigned char *digest) {
+  char *sign=oauth_encode_base64(len,digest);
+  char *sig_url = malloc(17+strlen(sign));
+  sprintf(sig_url,"oauth_body_hash=%s", sign);
+  free(sign);
+  free(digest);
+  return sig_url;
+}
+
+
+/**
+ * xep-0235 - TODO
+ */
+char *oauth_sign_xmpp (const char *xml,
+  OAuthMethod method, 
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_secret //< token secret - used as 2st part of secret-key
+	) {
+
+  return NULL;
+}
+
+// vi: sts=2 sw=2 ts=2
diff --git a/couchdb-glib/oauth.h b/couchdb-glib/oauth.h
new file mode 100644
index 0000000..5a289ff
--- /dev/null
+++ b/couchdb-glib/oauth.h
@@ -0,0 +1,539 @@
+/**
+ *  @brief oAuth.net implementation in POSIX-C.
+ *  @file oauth.h
+ *  @author Robin Gareus <robin gareus org>
+ *
+ * Copyright 2007, 2008, 2009 Robin Gareus <robin gareus org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+#ifndef _OAUTH_H
+#define _OAUTH_H      1 
+
+#ifndef DOXYGEN_IGNORE
+// liboauth version
+#define LIBOAUTH_VERSION "0.5.3"
+#define LIBOAUTH_VERSION_MAJOR  0
+#define LIBOAUTH_VERSION_MINOR  5
+#define LIBOAUTH_VERSION_MICRO  3
+
+//interface revision number
+//http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+#define LIBOAUTH_CUR  3
+#define LIBOAUTH_REV  0
+#define LIBOAUTH_AGE  3
+#endif
+
+#ifdef __GNUC__
+#    define OA_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > x || __GNUC__ == x && __GNUC_MINOR__ >= y)
+#else
+#    define OA_GCC_VERSION_AT_LEAST(x,y) 0
+#endif
+
+#ifndef attribute_deprecated
+#if OA_GCC_VERSION_AT_LEAST(3,1)
+#    define attribute_deprecated __attribute__((deprecated))
+#else
+#    define attribute_deprecated
+#endif
+#endif
+
+/** \enum OAuthMethod
+ * signature method to used for signing the request.
+ */ 
+typedef enum { 
+	OA_HMAC=0, ///< use HMAC-SHA1 request signing method
+	OA_RSA, ///< use RSA signature (not implemented)
+	OA_PLAINTEXT ///< use plain text signature (for testing only)
+	} OAuthMethod;
+
+/**
+ * Base64 encode and return size data in 'src'. The caller must free the
+ * returned string.
+ *
+ * @param size The size of the data in src
+ * @param src The data to be base64 encode
+ * @return encoded string otherwise NULL
+ */
+char *oauth_encode_base64(int size, const unsigned char *src);
+
+/**
+ * Decode the base64 encoded string 'src' into the memory pointed to by
+ * 'dest'. 
+ *
+ * @param dest Pointer to memory for holding the decoded string.
+ * Must be large enough to recieve the decoded string.
+ * @param src A base64 encoded string.
+ * @return the length of the decoded string if decode
+ * succeeded otherwise 0.
+ */
+int oauth_decode_base64(unsigned char *dest, const char *src);
+
+/**
+ * Escape 'string' according to RFC3986 and
+ * http://oauth.net/core/1.0/#encoding_parameters.
+ *
+ * @param string The data to be encoded
+ * @return encoded string otherwise NULL
+ * The caller must free the returned string.
+ */
+char *oauth_url_escape(const char *string);
+
+/**
+ * Parse RFC3986 encoded 'string' back to  unescaped version.
+ *
+ * @param string The data to be unescaped
+ * @param olen unless NULL the length of the returned string is stored there.
+ * @return decoded string or NULL
+ * The caller must free the returned string.
+ */
+char *oauth_url_unescape(const char *string, size_t *olen);
+ 
+
+/**
+ * returns base64 encoded HMAC-SHA1 signature for
+ * given message and key.
+ * both data and key need to be urlencoded.
+ *
+ * the returned string needs to be freed by the caller
+ *
+ * @param m message to be signed
+ * @param k key used for signing
+ * @return signature string.
+ */
+char *oauth_sign_hmac_sha1 (const char *m, const char *k);
+
+/**
+ * same as \ref oauth_sign_hmac_sha1 but allows
+ * to specify length of message and key (in case they contain null chars).
+ *
+ * @param m message to be signed
+ * @param ml length of message
+ * @param k key used for signing
+ * @param kl length of key
+ * @return signature string.
+ */
+char *oauth_sign_hmac_sha1_raw (const char *m, const size_t ml, const char *k, const size_t kl);
+
+/**
+ * returns plaintext signature for the given key.
+ *
+ * the returned string needs to be freed by the caller
+ *
+ * @param m message to be signed
+ * @param k key used for signing
+ * @return signature string
+ */
+char *oauth_sign_plaintext (const char *m, const char *k);
+
+/**
+ * returns RSA-SHA1 signature for given data.
+ * the returned signature needs to be freed by the caller.
+ *
+ * @param m message to be signed
+ * @param k private-key PKCS and Base64-encoded 
+ * @return base64 encoded signature string.
+ */
+char *oauth_sign_rsa_sha1 (const char *m, const char *k);
+
+/**
+ * verify RSA-SHA1 signature.
+ *
+ * returns the output of EVP_VerifyFinal() for a given message,
+ * cert/pubkey and signature.
+ *
+ * @param m message to be verified
+ * @param c public-key or x509 certificate
+ * @param s base64 encoded signature
+ * @return 1 for a correct signature, 0 for failure and -1 if some other error occurred
+ */
+int oauth_verify_rsa_sha1 (const char *m, const char *c, const char *s);
+
+/**
+ * url-escape strings and concatenate with '&' separator.
+ * The number of strings to be concatenated must be
+ * given as first argument.
+ * all arguments thereafter must be of type (char *) 
+ *
+ * @param len the number of arguments to follow this parameter
+ *
+ * @return pointer to memory holding the concatenated 
+ * strings - needs to be free(d) by the caller. or NULL
+ * in case we ran out of memory.
+ */
+char *oauth_catenc(int len, ...);
+
+/**
+ * splits the given url into a parameter array. 
+ * (see \ref oauth_serialize_url and \ref oauth_serialize_url_parameters for the reverse)
+ * (see \ref oauth_split_post_paramters for a more generic version)
+ *
+ * @param url the url or query-string to parse; may be NULL
+ * @param argv pointer to a (char *) array where the results are stored.
+ *  The array is re-allocated to match the number of parameters and each 
+ *  parameter-string is allocated with strdup. - The memory needs to be freed
+ *  by the caller.
+ * 
+ * @return number of parameter(s) in array.
+ */
+int oauth_split_url_parameters(const char *url, char ***argv);
+
+/**
+ * splits the given url into a parameter array. 
+ * (see \ref oauth_serialize_url and \ref oauth_serialize_url_parameters for the reverse)
+ *
+ * @param url the url or query-string to parse. 
+ * @param argv pointer to a (char *) array where the results are stored.
+ *  The array is re-allocated to match the number of parameters and each 
+ *  parameter-string is allocated with strdup. - The memory needs to be freed
+ *  by the caller.
+ * @param qesc use query parameter escape (vs post-param-escape) - if set
+ *        to 1 all '+' are treated as spaces ' '
+ * 
+ * @return number of parameter(s) in array.
+ */
+int oauth_split_post_paramters(const char *url, char ***argv, short qesc);
+
+/**
+ * build a url query string from an array.
+ *
+ * @param argc the total number of elements in the array
+ * @param start element in the array at which to start concatenating.
+ * @param argv parameter-array to concatenate.
+ * @return url string needs to be freed by the caller.
+ *
+ */
+char *oauth_serialize_url (int argc, int start, char **argv);
+
+/**
+ * encode query parameters from an array.
+ *
+ * @param argc the total number of elements in the array
+ * @param start element in the array at which to start concatenating.
+ * @param argv parameter-array to concatenate.
+ * @param sep separator for parameters (usually "&") 
+ * @return url string needs to be freed by the caller.
+ */
+char *oauth_serialize_url_sep (int argc, int start, char **argv, char *sep);
+
+/**
+ * build a query parameter string from an array.
+ *
+ * This function is a shortcut for \ref oauth_serialize_url (argc, 1, argv). 
+ * It strips the leading host/path, which is usually the first 
+ * element when using oauth_split_url_parameters on an URL.
+ *
+ * @param argc the total number of elements in the array
+ * @param argv parameter-array to concatenate.
+ * @return url string needs to be freed by the caller.
+ */
+char *oauth_serialize_url_parameters (int argc, char **argv);
+ 
+/**
+ * generate a random string between 15 and 32 chars length
+ * and return a pointer to it. The value needs to be freed by the
+ * caller
+ *
+ * @return zero terminated random string.
+ */
+char *oauth_gen_nonce();
+
+/**
+ * string compare function for oauth parameters.
+ *
+ * used with qsort. needed to normalize request parameters.
+ * see http://oauth.net/core/1.0/#anchor14
+ */
+int oauth_cmpstringp(const void *p1, const void *p2);
+
+
+/**
+ * search array for parameter key.
+ * @param argv length of array to search
+ * @param argc parameter array to search
+ * @param key key of parameter to check.
+ *
+ * @return FALSE (0) if array does not contain a paramater with given key, TRUE (1) otherwise.
+ */
+int oauth_param_exists(char **argv, int argc, char *key);
+
+/**
+ * add query parameter to array
+ *
+ * @param argcp pointer to array length int
+ * @param argvp pointer to array values 
+ * @param addparam parameter to add (eg. "foo=bar")
+ */
+void oauth_add_param_to_array(int *argcp, char ***argvp, const char *addparam);
+
+/**
+ * free array args
+ *
+ * @param argcp pointer to array length int
+ * @param argvp pointer to array values to be free()d
+ */
+void oauth_free_array(int *argcp, char ***argvp);
+
+/**
+ * calculate oAuth-signature for a given HTTP request URL, parameters and oauth-tokens.
+ *
+ * if 'postargs' is NULL a "GET" request is signed and the 
+ * signed URL is returned. Else this fn will modify 'postargs' 
+ * to point to memory that contains the signed POST-variables 
+ * and returns the base URL.
+ *
+ * both, the return value and (if given) 'postargs' need to be freed
+ * by the caller.
+ *
+ * @param url The request URL to be signed. append all GET or POST 
+ * query-parameters separated by either '?' or '&' to this parameter.
+ *
+ * @param postargs This parameter points to an area where the return value
+ * is stored. If 'postargs' is NULL, no value is stored.
+ *
+ * @param method specify the signature method to use. It is of type 
+ * \ref OAuthMethod and most likely \ref OA_HMAC.
+ *
+ * @param http_method The HTTP request method to use (ie "GET", "PUT",..)
+ * If NULL is given as 'http_method' this defaults to "GET" when 
+ * 'postargs' is also NULL and when postargs is not NULL "POST" is used.
+ *
+ * @param c_key consumer key
+ * @param c_secret consumer secret
+ * @param t_key token key
+ * @param t_secret token secret
+ *
+ * @return the signed url or NULL if an error occurred.
+ *
+ */
+char *oauth_sign_url2 (const char *url, char **postargs, 
+  OAuthMethod method, 
+  const char *http_method, //< HTTP request method
+  const char *c_key, //< consumer key - posted plain text
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_key, //< token key - posted plain text in URL
+  const char *t_secret //< token secret - used as 2st part of secret-key
+  );
+
+/**
+ * @deprecated Use oauth_sign_url2() instead.
+ */
+char *oauth_sign_url (const char *url, char **postargs, 
+  OAuthMethod method, 
+  const char *c_key, //< consumer key - posted plain text
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_key, //< token key - posted plain text in URL
+  const char *t_secret //< token secret - used as 2st part of secret-key
+  ) attribute_deprecated;
+
+/**
+ * same as /ref oauth_sign_url
+ * with the url already split into parameter array 
+ *
+ * @param argcp pointer to array length int
+ * @param argvp pointer to array values 
+ * (argv[0]="http://example.org:80/"; argv[1]="first=QueryParamater" ..)
+ *
+ * @param postargs This parameter points to an area where the return value
+ * is stored. If 'postargs' is NULL, no value is stored.
+ *
+ * @param method specify the signature method to use. It is of type 
+ * \ref OAuthMethod and most likely \ref OA_HMAC.
+ *
+ * @param http_method The HTTP request method to use (ie "GET", "PUT",..)
+ * If NULL is given as 'http_method' this defaults to "GET" when 
+ * 'postargs' is also NULL and when postargs is not NULL "POST" is used.
+ *
+ * @param c_key consumer key
+ * @param c_secret consumer secret
+ * @param t_key token key
+ * @param t_secret token secret
+ *
+ * @return the signed url or NULL if an error occurred.
+ */
+char *oauth_sign_array2 (int *argcp, char***argvp,
+  char **postargs,
+  OAuthMethod method, 
+  const char *http_method, //< HTTP request method
+  const char *c_key, //< consumer key - posted plain text
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_key, //< token key - posted plain text in URL
+  const char *t_secret //< token secret - used as 2st part of secret-key
+  );
+
+/**
+ * @deprecated Use oauth_sign_array2() instead.
+ */
+char *oauth_sign_array (int *argcp, char***argvp,
+  char **postargs,
+  OAuthMethod method, 
+  const char *c_key, //< consumer key - posted plain text
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_key, //< token key - posted plain text in URL
+  const char *t_secret //< token secret - used as 2st part of secret-key
+  ) attribute_deprecated;
+
+
+/** 
+ * calculate body hash (sha1sum) of given file and return
+ * a oauth_body_hash=xxxx parameter to be added to the request.
+ * The returned string needs to be freed by the calling function.
+ *
+ * see
+ * http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html
+ * 
+ * @param filename the filename to calculate the hash for
+ *
+ * @return URL oauth_body_hash parameter string
+ */
+char *oauth_body_hash_file(char *filename);
+
+/** 
+ * calculate body hash (sha1sum) of given data and return
+ * a oauth_body_hash=xxxx parameter to be added to the request.
+ * The returned string needs to be freed by the calling function.
+ * The returned string is not yet url-escaped and suitable to be 
+ * passed as argument to \ref oauth_catenc.
+ *
+ * see
+ * http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html
+ * 
+ * @param length length of the data parameter in bytes
+ * @param data to calculate the hash for
+ *
+ * @return URL oauth_body_hash parameter string
+ */
+char *oauth_body_hash_data(size_t length, const char *data);
+
+/**
+ * base64 encode digest, free it and return a URL parameter
+ * with the oauth_body_hash. The returned hash needs to be freed by the
+ * calling function. The returned string is not yet url-escaped and 
+ * thus suitable to be passed to \ref oauth_catenc.
+ *
+ * @param len length of the digest to encode
+ * @param digest hash value to encode
+ *
+ * @return URL oauth_body_hash parameter string
+ */
+char *oauth_body_hash_encode(size_t len, unsigned char *digest);
+
+/**
+ * xep-0235 - TODO
+ */
+char *oauth_sign_xmpp (const char *xml,
+  OAuthMethod method, 
+  const char *c_secret, //< consumer secret - used as 1st part of secret-key 
+  const char *t_secret //< token secret - used as 2st part of secret-key
+  );
+
+/**
+ * do a HTTP GET request, wait for it to finish 
+ * and return the content of the reply.
+ * (requires libcurl or a command-line HTTP client)
+ *
+ * If compiled <b>without</b> libcurl this function calls
+ * a command-line executable defined in the environment variable
+ * OAUTH_HTTP_GET_CMD - it defaults to 
+ * <tt>curl -sA 'liboauth-agent/0.1' '%%u'</tt>
+ * where %%u is replaced with the URL and query parameters.
+ *
+ * bash & wget example:
+ * <tt>export OAUTH_HTTP_CMD="wget -q -U 'liboauth-agent/0.1' '%%u' "</tt>
+ *
+ * WARNING: this is a tentative function. it's convenient and handy for testing
+ * or developing oAuth code. But don't rely on this function
+ * to become a stable part of this API. It does not do 
+ * much error checking or handling for one thing..
+ *
+ * NOTE: \a u and \a q are just concatenated with a '?' in between,
+ * unless \a q is NULL. in which case only \a u will be used.
+ *
+ * @param u base url to get
+ * @param q query string to send along with the HTTP request or NULL.
+ * @return  In case of an error NULL is returned; otherwise a pointer to the
+ * replied content from HTTP server. latter needs to be freed by caller.
+ */
+char *oauth_http_get (const char *u, const char *q);
+
+
+/**
+ * do a HTTP POST request, wait for it to finish 
+ * and return the content of the reply.
+ * (requires libcurl or a command-line HTTP client)
+ *
+ * If compiled <b>without</b> libcurl this function calls
+ * a command-line executable defined in the environment variable
+ * OAUTH_HTTP_CMD - it defaults to 
+ * <tt>curl -sA 'liboauth-agent/0.1' -d '%%p' '%%u'</tt>
+ * where %%p is replaced with the postargs and %%u is replaced with 
+ * the URL. 
+ *
+ * bash & wget example:
+ * <tt>export OAUTH_HTTP_CMD="wget -q -U 'liboauth-agent/0.1' --post-data='%%p' '%%u' "</tt>
+ *
+ * NOTE: This function uses the curl's default HTTP-POST Content-Type:
+ * application/x-www-form-urlencoded which is the only option allowed
+ * by oauth core 1.0 spec. Experimental code can use the Environment variable
+ * to transmit custom HTTP headers or parameters.
+ *
+ * WARNING: this is a tentative function. it's convenient and handy for testing
+ * or developing oAuth code. But don't rely on this function
+ * to become a stable part of this API. It does not do 
+ * much error checking for one thing..
+ *
+ * @param u url to query
+ * @param p postargs to send along with the HTTP request.
+ * @return replied content from HTTP server. needs to be freed by caller.
+ */
+char *oauth_http_post (const char *u, const char *p);
+
+/**
+ * http post raw data from file.
+ * the returned string needs to be freed by the caller
+ * (requires libcurl)
+ *
+ * see dislaimer: /ref oauth_http_post
+ *
+ * @param u url to retrieve
+ * @param fn filename of the file to post along
+ * @param len length of the file in bytes. set to '0' for autodetection
+ * @param customheader specify custom HTTP header (or NULL for default)
+ * @return returned HTTP reply or NULL on error
+ */
+char *oauth_post_file (const char *u, const char *fn, size_t len, const char *customheader);
+
+/**
+ * http post raw data
+ * the returned string needs to be freed by the caller
+ * (requires libcurl)
+ *
+ * see dislaimer: /ref oauth_http_post
+ *
+ * @param u url to retrieve
+ * @param data data to post
+ * @param len length of the data in bytes. 
+ * @param customheader specify custom HTTP header (or NULL for default)
+ * @return returned HTTP reply or NULL on error
+ */
+char *oauth_post_data (const char *u, const char *data, size_t len, const char *customheader);
+
+#endif
+/* vi:set ts=8 sts=2 sw=2: */
diff --git a/couchdb-glib/utils.c b/couchdb-glib/utils.c
index ef5ecfd..fce7aa0 100644
--- a/couchdb-glib/utils.c
+++ b/couchdb-glib/utils.c
@@ -24,6 +24,10 @@
 #include <libsoup/soup-session-async.h>
 #include "couchdb-glib.h"
 #include "utils.h"
+#ifdef HAVE_OAUTH
+#include <time.h>
+#include "oauth.h"
+#endif
 
 static JsonParser *
 parse_json_response (SoupMessage *http_message, GError **error)
@@ -72,6 +76,77 @@ couchdb_error_quark (void)
 	return error;
 }
 
+#ifdef HAVE_OAUTH
+static void
+add_oauth_signature (CouchDB *couchdb, SoupMessage *http_message, const char *method, const char *url)
+{
+	char *signed_url;
+
+	signed_url = oauth_sign_url2 (url, NULL, OA_HMAC, method,
+				      couchdb->oauth_consumer_key,
+				      couchdb->oauth_consumer_secret,
+				      couchdb->oauth_token_key,
+				      couchdb->oauth_token_secret);
+	if (signed_url != NULL) {
+		char **parsed_url;
+		GString *header = NULL;
+
+		/* Get the OAuth signature from the signed URL */
+		parsed_url = g_strsplit (signed_url, "?", 2);
+		if (parsed_url != NULL) {
+			gchar **params;
+			int i;
+
+			params = g_strsplit (parsed_url[1], "&", 0);
+#ifdef DEBUG_OAUTH
+			g_debug ("Parsing %s", parsed_url[1]);
+#endif
+			for (i = 0; params[i] != NULL; i++) {
+				gchar **url_param;
+
+#ifdef DEBUG_OAUTH
+				g_debug ("%s\n", params[i]);
+#endif
+				url_param = g_strsplit (params[i], "=", 2);
+				if (url_param == NULL)
+					continue;
+
+				if (header != NULL)
+					header = g_string_append (header, ", ");
+				else
+					header = g_string_new ("OAuth ");
+
+				header = g_string_append (header, url_param[0]);
+				header = g_string_append (header, "=\"");
+				header = g_string_append (header, url_param[1]);
+				header = g_string_append (header, "\"");
+
+				g_strfreev (url_param);
+			}
+
+			if (params)
+				g_strfreev (params);
+
+			g_strfreev (parsed_url);
+		}
+
+		if (header != NULL) {
+			soup_message_headers_append (http_message->request_headers, "Authorization", header->str);
+
+			g_string_free (header, TRUE);
+		}
+
+		free (signed_url);
+	}
+}
+#endif
+
+static void
+debug_print_headers (const char *name, const char *value, gpointer user_data)
+{
+	g_print ("\t%s: %s\n", name, value);
+}
+
 JsonParser *
 send_message_and_parse (CouchDB *couchdb, const char *method, const char *url, const char *body, GError **error)
 {
@@ -85,12 +160,21 @@ send_message_and_parse (CouchDB *couchdb, const char *method, const char *url, c
 					  body, strlen (body));
 	}
 
-	g_debug ("Sending %s to %s...", method, url);
+#ifdef HAVE_OAUTH
+	if (couchdb->oauth_enabled)
+		add_oauth_signature (couchdb, http_message, method, url);
+#endif
+
+	g_debug ("Sending %s to %s... with headers\n: ", method, url);
+	soup_message_headers_foreach (http_message->request_headers,
+				      (SoupMessageHeadersForeachFunc) debug_print_headers,
+				      NULL);
+
 	status = soup_session_send_message (couchdb->http_session, http_message);
 	if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
 	       	parser = parse_json_response (http_message, error);
 	} else {
-		g_set_error (error, COUCHDB_ERROR, status, http_message->reason_phrase);
+		g_set_error (error, COUCHDB_ERROR, status, "%s", http_message->reason_phrase);
 	}
 
 	return parser;
diff --git a/couchdb-glib/utils.h b/couchdb-glib/utils.h
index 7157bdf..d3f124a 100644
--- a/couchdb-glib/utils.h
+++ b/couchdb-glib/utils.h
@@ -22,6 +22,7 @@
 #ifndef __UTILS_H__
 #define __UTILS_H__
 
+#include "config.h"
 #include <libsoup/soup-session-async.h>
 #include <json-glib/json-glib.h>
 
@@ -32,6 +33,14 @@ struct _CouchDB {
 	SoupSession *http_session;
 
 	GHashTable *db_watchlist;
+
+#ifdef HAVE_OAUTH
+	gboolean oauth_enabled;
+	char *oauth_consumer_key;
+	char *oauth_consumer_secret;
+	char *oauth_token_key;
+	char *oauth_token_secret;
+#endif
 };
 
 struct _CouchDBDocument {
diff --git a/couchdb-glib/xmalloc.c b/couchdb-glib/xmalloc.c
new file mode 100644
index 0000000..a7e340f
--- /dev/null
+++ b/couchdb-glib/xmalloc.c
@@ -0,0 +1,151 @@
+/* xmalloc.c -- malloc with out of memory checking
+   Copyright (C) 1990, 91, 92, 93, 94, 95, 96, 99 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef USE_LGPL
+// TODO better use #define in header file?!
+#include <string.h>
+#include <stdlib.h>
+void *xmalloc (size_t n) {return malloc(n);}
+void *xcalloc (size_t n, size_t s) {return calloc(n,s);}
+void *xrealloc (void *p, size_t n) {return realloc(p,n);}
+char *xstrdup (const char *p) {return strdup(p);}
+
+#else // LGPL LICENSED CODE 
+#if __STDC__
+# define VOID void
+#else
+# define VOID char
+#endif
+
+#include <stdio.h>		/* for stderr */
+
+#if STDC_HEADERS
+
+#include <sys/types.h>
+#include <string.h>		/* for strlen etc. */
+#include <stdlib.h>
+
+#else  /* !STDC_HEADERS */
+
+extern size_t strlen ();
+extern char *strcpy ();
+
+VOID *calloc ();
+VOID *malloc ();
+VOID *realloc ();
+void free ();
+#endif
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# define textdomain(Domain)
+# define _(Text) Text
+#endif
+
+/* Prototypes for functions defined here.  */
+#if defined (__STDC__) && __STDC__
+static VOID *fixup_null_alloc (size_t n);
+VOID *xmalloc (size_t n);
+VOID *xcalloc (size_t n, size_t s);
+VOID *xrealloc (VOID *p, size_t n);
+char *xstrdup (const char *p);
+#endif
+
+
+static VOID *
+fixup_null_alloc (n)
+     size_t n;
+{
+  VOID *p;
+
+  p = 0;
+  if (n == 0)
+    p = malloc ((size_t) 1);
+  if (p == 0)
+    {
+      /* possible revisions: release some memory and re-try, print
+	 more information (e.g. line number of input file) */
+      fprintf(stderr, _("liboauth: Memory exhausted"));
+      exit(1);
+    }
+  return p;
+}
+
+/* Allocate N bytes of memory dynamically, with error checking.  */
+
+VOID *
+xmalloc (n)
+     size_t n;
+{
+  VOID *p;
+
+  p = malloc (n);
+  if (p == 0)
+    p = fixup_null_alloc (n);
+  return p;
+}
+
+/* Allocate memory for N elements of S bytes, with error checking.  */
+
+VOID *
+xcalloc (n, s)
+     size_t n, s;
+{
+  VOID *p;
+
+  p = calloc (n, s);
+  if (p == 0)
+    p = fixup_null_alloc (n);
+  return p;
+}
+
+/* Change the size of an allocated block of memory P to N bytes,
+   with error checking.
+   If P is NULL, run xmalloc.  */
+
+VOID *
+xrealloc (p, n)
+     VOID *p;
+     size_t n;
+{
+  if (p == 0)
+    return xmalloc (n);
+  p = realloc (p, n);
+  if (p == 0)
+    p = fixup_null_alloc (n);
+  return p;
+}
+
+/* Make a copy of a string in a newly allocated block of memory. */
+
+char *
+xstrdup (str)
+     const char *str;
+{
+  VOID *p;
+
+  p = xmalloc (strlen (str) + 1);
+  strcpy (p, str);
+  return p;
+}
+#endif
diff --git a/couchdb-glib/xmalloc.h b/couchdb-glib/xmalloc.h
new file mode 100644
index 0000000..3b06442
--- /dev/null
+++ b/couchdb-glib/xmalloc.h
@@ -0,0 +1,12 @@
+/* Prototypes for functions defined in xmalloc.c  */
+
+void *xmalloc (size_t n);
+void *xcalloc (size_t n, size_t s);
+void *xrealloc (void *p, size_t n);
+char *xstrdup (const char *p);
+
+/* POSIX prototypes - avoid compiler warnings with '-posix' */
+int strncasecmp(const char *s1, const char *s2, size_t n);
+int snprintf(char *str, size_t size, const char *format, ...);
+FILE *popen(const char *command, const char *type);
+int pclose(FILE *stream);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1d657aa..fb9f7b0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,21 +4,31 @@ INCLUDES =			\
 
 noinst_PROGRAMS = 		\
 	test-couchdb-glib	\
-	test-list-databases
+	test-list-databases	\
+	test-oauth
 
 test_list_databases_SOURCES = test-list-databases.c
 test_list_databases_LDADD = 	\
 	$(COUCHDB_GLIB_LIBS)	\
+	$(OAUTH_LIBS)		\
 	-luuid			\
 	$(top_builddir)/couchdb-glib/libcouchdb-glib-1.0.la
 
 test_couchdb_glib_SOURCES = test-couchdb-glib.c
 test_couchdb_glib_LDADD = 	\
 	$(COUCHDB_GLIB_LIBS)	\
+	$(OAUTH_LIBS)		\
 	-luuid			\
 	$(top_builddir)/couchdb-glib/libcouchdb-glib-1.0.la
 
-EXTRA_DIST = createCouchContacts.py
+test_oauth_SOURCES = test-oauth.c
+test_oauth_LDADD = 	\
+	$(COUCHDB_GLIB_LIBS)	\
+	$(OAUTH_LIBS)		\
+	-luuid			\
+	$(top_builddir)/couchdb-glib/libcouchdb-glib-1.0.la
+
+EXTRA_DIST = createCouchContacts.py test-oauth.py
 
 check_PROGRAMS = test-couchdb-glib
 
diff --git a/tests/test-oauth.c b/tests/test-oauth.c
new file mode 100644
index 0000000..ec2f36b
--- /dev/null
+++ b/tests/test-oauth.c
@@ -0,0 +1,134 @@
+#include <string.h>
+#include <glib.h>
+#include <libsoup/soup-uri.h>
+#include <oauth.h>
+#include <openssl/hmac.h>
+#include <couchdb-glib.h>
+
+void
+usage (const char *program)
+{
+	g_print ("Usage: %s consumer_key consumer_secret token_key token_secret url method\n", program);
+	exit (-1);
+}
+
+char *
+openssl_sign (const char *base_signature, const char *key)
+{
+	unsigned char result[EVP_MAX_MD_SIZE];
+	unsigned int resultlen = 0;
+  
+	HMAC(EVP_sha1(), key, strlen (key), 
+	     (unsigned char*) base_signature, strlen (base_signature),
+	     result, &resultlen);
+
+	return g_base64_encode ((guchar *) result, resultlen);
+}
+
+void
+retrieve_nonce_and_timestamp (const char *sign, char **nonce, char **timestamp)
+{
+	char **parsed_url;
+
+	*nonce = NULL;
+	*timestamp = NULL;
+
+	parsed_url = g_strsplit (sign, "?", 2);
+	if (parsed_url) {
+		gchar **params;
+		int i;
+
+		params = g_strsplit ((const gchar *) parsed_url[1], "&", 0);
+		for (i = 0; params[i] != NULL; i++) {
+			gchar **url_param;
+
+			url_param = g_strsplit ((const gchar *) params[i], "=", 2);
+			if (url_param == NULL)
+				continue;
+
+			if (g_strcmp0 (url_param[0], "oauth_nonce") == 0)
+				*nonce = g_strdup (url_param[1]);
+			else if (g_strcmp0 (url_param[0], "oauth_timestamp") == 0)
+				*timestamp = g_strdup (url_param[1]);
+
+			g_strfreev (url_param);
+		}
+
+		g_strfreev (params);
+		g_strfreev (parsed_url);
+	}
+}
+
+int
+main (int argc, char *argv[])
+{
+	char *base_signature, *oauth_url, *encoded_oauth_url, *oauth_key;
+	char *c_key, *c_secret, *t_key, *t_secret, *url, *method, *nonce, *timestamp;
+	char *liboauth_signed;
+	char *command_line, *command_line_output;
+	CouchDB *couchdb;
+	GSList *db_list;
+	GError *error = NULL;
+
+	if (argc != 7)
+		usage (argv[0]);
+
+	c_key = argv[1];
+	c_secret = argv[2];
+	t_key = argv[3];
+	t_secret = argv[4];
+	url = argv[5];
+	method = argv[6];
+
+	/* First run liboauth signing, which generates nonce and timestamp */
+	g_print ("*** Signing with liboauth ***\n");
+	liboauth_signed = oauth_sign_url2 (url, NULL, OA_HMAC, method, c_key, c_secret, t_key, t_secret);
+	g_print ("oauth_sign_url2 returns: %s\n", liboauth_signed);
+
+	retrieve_nonce_and_timestamp (liboauth_signed, &nonce, &timestamp);
+
+	/* Running openssl signing */
+	g_print ("\n*** Signing with openssl ***\n");
+	oauth_url = g_strdup_printf (
+		"oauth_callback=None&oauth_consumer_key=%s&oauth_nonce=%s&oauth_signature_method=HMAC-SHA1&oauth_timestamp=%s&oauth_token=%s&oauth_verifier=None&oauth_version=1.0",
+		c_key, nonce, timestamp, t_key);
+	encoded_oauth_url = soup_uri_encode (oauth_url, "=&");
+	base_signature = g_strdup_printf ("%s&%s&%s", method, soup_uri_encode (url, NULL), encoded_oauth_url);
+	oauth_key = g_strdup_printf ("%s&%s", c_secret, t_secret);
+
+	g_print ("openssl: data to sign='%s'\n", base_signature);
+	g_print ("openssl: key='%s'\n", oauth_key);
+	g_print ("openssl returns: %s\n", openssl_sign (base_signature, oauth_key));
+
+	/* Running python-oauth script */
+	g_print ("\n*** Signing with python-oauth ***\n");
+	command_line = g_strdup_printf ("/usr/bin/python test-oauth.py %s %s %s %s %s %s %s %s",
+					c_key, c_secret, t_key, t_secret, url, method, nonce, timestamp);
+	g_spawn_command_line_sync (command_line, &command_line_output, NULL, NULL, NULL);
+	g_print ("%s\n", command_line_output);
+
+	/* Now connecting to CouchDB with this OAuth stuff */
+	g_type_init ();
+	g_thread_init (NULL);
+
+	couchdb = couchdb_new (url);
+	couchdb_enable_oauth (couchdb, c_key, c_secret, t_key, t_secret);
+
+	db_list = couchdb_list_databases (couchdb, &error);
+	if (db_list != NULL) {
+		GSList *sl;
+
+		for (sl = db_list; sl != NULL; sl = sl->next) {
+			g_print ("Found database %s\n", (const char *) sl->data);
+		}
+
+		couchdb_free_database_list (db_list);
+	} else if (error != NULL) {
+		g_print ("Could not get list of databases: %s\n", error->message);
+		g_error_free (error);
+	}
+
+	g_object_unref (G_OBJECT (couchdb));
+
+	return 0;
+}
diff --git a/tests/test-oauth.py b/tests/test-oauth.py
new file mode 100644
index 0000000..592e5a0
--- /dev/null
+++ b/tests/test-oauth.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+import sys
+from oauth import oauth
+
+(consumer_key, consumer_secret, actual_token, token_secret, url,
+	method, nonce, timestamp) = sys.argv[1:]
+
+consumer=oauth.OAuthConsumer(consumer_key, consumer_secret)
+token=oauth.OAuthToken(actual_token, token_secret);
+rq=oauth.OAuthRequest.from_consumer_and_token(consumer, token=token,
+	http_method=method, http_url=url,
+        parameters={'oauth_nonce':nonce,'oauth_timestamp':timestamp});
+parameters = {
+    'oauth_consumer_key': consumer_key,
+    'oauth_nonce': nonce,
+    'oauth_signature_method': 'HMAC-SHA1',
+    'oauth_timestamp': timestamp,
+    'oauth_token': actual_token,
+    'oauth_version': '1.0'
+    }
+#rq = oauth.OAuthRequest(method, url, parameters)
+rq.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),consumer, token);
+
+headers = rq.to_header()
+oauth_url = rq.to_url()
+print rq.get_normalized_parameters()
+print oauth_url
+print headers
+parts = [x.strip() for x in headers["Authorization"].split(",") if x.find("=") != -1]
+smallparts = dict([x.split("=") for x in parts])
+print "Signature from Python:", smallparts["oauth_signature"]



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]