Index: Dev/trunk/src/server/util/couch.coffee
===================================================================
--- Dev/trunk/src/server/util/couch.coffee	(revision 479)
+++ Dev/trunk/src/server/util/couch.coffee	(revision 487)
@@ -6,32 +6,42 @@
  
 class CouchDB
-    constructor: (url) ->
-        @url = normalizeURL url
-    get: (id) ->
+    constructor: (url,db) ->
+        @serverURL = normalizeURL url
+        @db = db
+        if @db
+            @url = "#{@serverURL}/#{@db}"
+        else
+            @url = "#{@serverURL}"
+    get: (id, opts) ->
         url = "#{@url}/#{id}"
-        couchRequest 'GET', url
-    post: (doc) ->
-        url = "#{@url}"
-        couchRequest 'POST', url, doc
-    put: (id, doc) ->
+        couchRequest 'GET', url, null, opts
+    post: (doc, opts) ->
+        url = "#{@url}/"
+        couchRequest 'POST', url, doc, opts
+    put: (id, doc, opts) ->
         url = "#{@url}/#{id}"
-        couchRequest 'PUT', url, doc
-    delete: (id, rev) ->
-        url = "#{@url}/#{id}?rev=#{rev}"
-        couchRequest 'DELETE', url
+        couchRequest 'PUT', url, doc, opts
+    delete: (id, opts) ->
+        url = "#{@url}/#{id}"
+        couchRequest 'DELETE', url, null, opts
+    uuids: (opts) ->
+        url = "#{@serverURL}/_uuids"
+        couchRequest 'GET', url, null, opts
 
 normalizeURL = (url) ->
     url.replace /\/$/, ''
 
-couchRequest = (method, url, data) ->
+couchRequest = (method, url, body, opts) ->
     options =
         method: method,
         headers:
-            'content-type': 'application/json; charset=utf-8'
-            'accept': 'application/json'
-        body: JSON.stringify (stringifyFunctions (data || {}))
+            'Content-Type': 'application/json; charset=utf-8'
+            'Accept': 'application/json'
+        body: JSON.stringify (stringifyFunctions (body || {}))
+    if opts
+        options.qs = createQueryObj opts.query if opts.query
+        _.extend options.headers, opts.headers if opts.headers
     req = request(url, options)
-    req.response
-    .then (res) =>
+    res = req.response.then (res) =>
         req.then (res) =>
             JSON.parse res
@@ -40,5 +50,18 @@
     , (err) =>
         Q.reject err
+    res.response = req.response.then (res) =>
+        statusCode: res.statusCode
+        headers: res.headers
+        body: JSON.parse res.body
+    , (err) =>
+        Q.reject err
+    res
 
+createQueryObj = (obj) ->
+    newObj = {}
+    _.each obj, (val,key) ->
+        newObj[key] = JSON.stringify val
+    newObj
+        
 stringifyFunctions = (value) ->
     if value is null
Index: Dev/trunk/src/server/util/crypto-token.js
===================================================================
--- Dev/trunk/src/server/util/crypto-token.js	(revision 487)
+++ Dev/trunk/src/server/util/crypto-token.js	(revision 487)
@@ -0,0 +1,14 @@
+var Q = require('q')
+  , crypto = require('crypto');
+
+module.exports = function(bytes) {
+    var q = Q.defer();
+    crypto.randomBytes(bytes || 8, function(ex, buf) {
+        if ( ex ) {
+            q.reject(ex);
+        } else {
+            q.resolve(buf.toString('hex'));
+        }
+    });
+    return q.promise;
+};
Index: Dev/trunk/src/server/util/etags.js
===================================================================
--- Dev/trunk/src/server/util/etags.js	(revision 487)
+++ Dev/trunk/src/server/util/etags.js	(revision 487)
@@ -0,0 +1,14 @@
+var _ = require('underscore');
+
+var revRe = /"([^"]*)"/g;
+
+exports.parse = function(header) {
+    return _.map((header && header.match(revRe)) || [], function(str){
+        return str.substring(1,str.length-1);
+    });
+};
+
+exports.format = function(etags) {
+    return _.map(etags || [], function(str) { return '"'+str+'"'; })
+            .join(', ');
+};
Index: Dev/trunk/src/server/util/http-result.js
===================================================================
--- Dev/trunk/src/server/util/http-result.js	(revision 487)
+++ Dev/trunk/src/server/util/http-result.js	(revision 487)
@@ -0,0 +1,78 @@
+// <init> :: status? -> result? -> HTTPResult
+function HTTPResult(status,result) {
+    this.status = 0;
+    this.result = null;
+    this._handlers = [];
+    if ( status ) {
+        this.set(status,result);
+    }
+}
+
+HTTPResult.fromResponsePromise = function(promise,errorHandler) {
+    var result = new HTTPResult();
+    promise.then(function(response){
+        result.set(response.statusCode,response.body);
+    }, errorHandler && function(error){
+        var ret = errorHandler(error);
+        if ( ret.constructor === HTTPResult ) {
+            ret.handle(result.set.bind(result));
+        } else {
+            result.set(500,ret);
+        }
+    });
+    return result;
+};
+
+// isSet :: Bool
+HTTPResult.prototype.isSet = function() {
+    return this.status !== 0;
+};
+
+// set :: status -> result? -> ()
+HTTPResult.prototype.set = function(status,result) {
+    if ( this.isSet() ) { throw new Error("Result was already set."); }
+    if ( typeof status !== 'number' ) { throw new Error("Status must be a number."); }
+    this.status = status;
+    this.result = result;
+    for ( var i = 0; i < this._handlers.length; i++ ) {
+        this._fire(this._handlers[i]);
+    }
+};
+
+// _fire :: (status -> result -> Either HTTPResult Any) -> ()
+HTTPResult.prototype._fire = function(f) {
+    var ret = f(this.status,this.result);
+    if ( ret.constructor === HTTPResult ) {
+        ret.handle(f._next.set.bind(f._next));
+    } else {
+        f._next.set(this.status,ret);
+    }
+};
+
+/* handle :: Either (status -> result -> Either HTTPResult Any)
+ *                  {number: (result -> Either HTTPResult Any),
+ *                   'default': (status -> result -> Either HTTPResult Any)}
+ *           -> HTTPResult
+ */
+HTTPResult.prototype.handle = function(fOrObj) {
+    var f = typeof fOrObj === 'function' ?
+            fOrObj :
+            function(status,result) {
+                if ( status in fOrObj ) {
+                    return fOrObj[status](result);
+                } else if ( 'default' in fOrObj ) {
+                    return fOrObj['default'](status,result);
+                } else {
+                    return result;
+                }
+            };
+    f._next = new HTTPResult();
+    if ( this.isSet() ) {
+        this._fire(f);
+    } else {
+        this._handlers.push(f);
+    }
+    return f._next;
+};
+
+module.exports = HTTPResult;
