[couchdb-glib] Added OAuth support:
- From: Rodrigo Moya <rodrigo src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [couchdb-glib] Added OAuth support:
- Date: Wed, 9 Sep 2009 14:54:27 +0000 (UTC)
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, ×tamp);
+
+ /* 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]