summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Joseph Davis <davisp@apache.org>2009-11-26 19:31:00 +0000
committerPaul Joseph Davis <davisp@apache.org>2009-11-26 19:31:00 +0000
commit95ee619df135a4c8b3ecefe65503c6d1cc7c36da (patch)
tree16fe8cecc8237e55fe4eec53198403a114b702b4 /src
parentc452048cae6ff8b275e72a2f4b590ccaeebc35ba (diff)
Complete refactoring of couch_js.
In particular, the cURL bindings have been rewritten to be more useful and easily applied in command line scripts. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@884672 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/couchdb/priv/Makefile.am32
-rw-r--r--src/couchdb/priv/couch_js/couch_js.c1286
-rw-r--r--src/couchdb/priv/couch_js/curlhelper.c255
-rw-r--r--src/couchdb/priv/couch_js/curlhelper.h49
-rw-r--r--src/couchdb/priv/couch_js/http.c647
-rw-r--r--src/couchdb/priv/couch_js/http.h18
-rw-r--r--src/couchdb/priv/couch_js/main.c324
-rw-r--r--src/couchdb/priv/couch_js/utf8.c286
-rw-r--r--src/couchdb/priv/couch_js/utf8.h19
9 files changed, 1325 insertions, 1591 deletions
diff --git a/src/couchdb/priv/Makefile.am b/src/couchdb/priv/Makefile.am
index d4c759c1..8c4383dc 100644
--- a/src/couchdb/priv/Makefile.am
+++ b/src/couchdb/priv/Makefile.am
@@ -15,11 +15,41 @@ couchprivdir = $(couchlibdir)/priv
couchprivlibdir = $(couchlibdir)/priv/lib
EXTRA_DIST = \
- couchspawnkillable.sh \
+ spawnkillable/couchspawnkillable.sh \
stat_descriptions.cfg.in
CLEANFILES = stat_descriptions.cfg
+ICU_LOCAL_FLAGS = $(ICU_LOCAL_CFLAGS) $(ICU_LOCAL_LDFLAGS)
+if WINDOWS
+ICU_LOCAL_LIBS=-licuuc -licudt -licuin
+else
+ICU_LOCAL_LIBS=-licuuc -licudata -licui18n
+endif
+
+couchprivlib_LTLIBRARIES = couch_icu_driver.la
+couch_icu_driver_la_SOURCES = icu_driver/couch_icu_driver.c
+couch_icu_driver_la_LDFLAGS = -module -avoid-version $(ICU_LOCAL_FLAGS)
+couch_icu_driver_la_CFLAGS = $(ICU_LOCAL_FLAGS)
+couch_icu_driver_la_LIBADD = $(ICU_LOCAL_LIBS)
+
+if WINDOWS
+couch_icu_driver_la_LDFLAGS += -no-undefined
+endif
+
+COUCHJS_SRCS = \
+ couch_js/http.c \
+ couch_js/http.h \
+ couch_js/main.c \
+ couch_js/utf8.c \
+ couch_js/utf8.h
+
+locallibbin_PROGRAMS = couchjs
+couchjs_SOURCES = $(COUCHJS_SRCS)
+couchjs_LDFLAGS = $(CURL_LDFLAGS)
+couchjs_CFLAGS = $(CURL_CFLAGS)
+couchjs_LDADD = $(CURL_LDFLAGS) @JSLIB@
+
couchpriv_DATA = stat_descriptions.cfg
couchpriv_PROGRAMS = couchspawnkillable
diff --git a/src/couchdb/priv/couch_js/couch_js.c b/src/couchdb/priv/couch_js/couch_js.c
deleted file mode 100644
index 0acc5b55..00000000
--- a/src/couchdb/priv/couch_js/couch_js.c
+++ /dev/null
@@ -1,1286 +0,0 @@
-/*
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use
-this file except in compliance with the License. You may obtain a copy of the
-License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software distributed
-under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
-CONDITIONS OF ANY KIND, either express or implied. See the License for the
-specific language governing permissions and limitations under the License.
-
-*/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "curlhelper.h"
-#include <jsapi.h>
-#include <curl/curl.h>
-#include "config.h"
-
-#ifndef CURLOPT_COPYPOSTFIELDS
- #define CURLOPT_COPYPOSTFIELDS 10165
-#endif
-
-int gExitCode = 0;
-int gStackChunkSize = 8L * 1024L;
-
-int
-EncodeChar(uint8 *utf8Buffer, uint32 ucs4Char) {
- int utf8Length = 1;
-
- if (ucs4Char < 0x80) {
- *utf8Buffer = (uint8)ucs4Char;
- } else {
- int i;
- uint32 a = ucs4Char >> 11;
- utf8Length = 2;
- while (a) {
- a >>= 5;
- utf8Length++;
- }
- i = utf8Length;
- while (--i) {
- utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80);
- ucs4Char >>= 6;
- }
- *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
- }
- return utf8Length;
-}
-
-JSBool
-EncodeString(const jschar *src, size_t srclen, char *dst, size_t *dstlenp) {
- size_t i, utf8Len, dstlen = *dstlenp, origDstlen = dstlen;
- jschar c, c2;
- uint32 v;
- uint8 utf8buf[6];
-
- if (!dst)
- dstlen = origDstlen = (size_t) -1;
-
- while (srclen) {
- c = *src++;
- srclen--;
- if ((c >= 0xDC00) && (c <= 0xDFFF))
- goto badSurrogate;
- if (c < 0xD800 || c > 0xDBFF) {
- v = c;
- } else {
- if (srclen < 1)
- goto bufferTooSmall;
- c2 = *src++;
- srclen--;
- if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
- c = c2;
- goto badSurrogate;
- }
- v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
- }
- if (v < 0x0080) {
- /* no encoding necessary - performance hack */
- if (!dstlen)
- goto bufferTooSmall;
- if (dst)
- *dst++ = (char) v;
- utf8Len = 1;
- } else {
- utf8Len = EncodeChar(utf8buf, v);
- if (utf8Len > dstlen)
- goto bufferTooSmall;
- if (dst) {
- for (i = 0; i < utf8Len; i++)
- *dst++ = (char) utf8buf[i];
- }
- }
- dstlen -= utf8Len;
- }
- *dstlenp = (origDstlen - dstlen);
- return JS_TRUE;
-
-badSurrogate:
- *dstlenp = (origDstlen - dstlen);
- return JS_FALSE;
-
-bufferTooSmall:
- *dstlenp = (origDstlen - dstlen);
- return JS_FALSE;
-}
-
-static uint32
-DecodeChar(const uint8 *utf8Buffer, int utf8Length) {
- uint32 ucs4Char;
- uint32 minucs4Char;
- /* from Unicode 3.1, non-shortest form is illegal */
- static const uint32 minucs4Table[] = {
- 0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000
- };
-
- if (utf8Length == 1) {
- ucs4Char = *utf8Buffer;
- } else {
- ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
- minucs4Char = minucs4Table[utf8Length-2];
- while (--utf8Length) {
- ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
- }
- if (ucs4Char < minucs4Char ||
- ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) {
- ucs4Char = 0xFFFD;
- }
- }
- return ucs4Char;
-}
-
-JSBool
-DecodeString(const char *src, size_t srclen, jschar *dst, size_t *dstlenp) {
- uint32 v;
- size_t offset = 0, j, n, dstlen = *dstlenp, origDstlen = dstlen;
-
- if (!dst)
- dstlen = origDstlen = (size_t) -1;
-
- while (srclen) {
- v = (uint8) *src;
- n = 1;
- if (v & 0x80) {
- while (v & (0x80 >> n))
- n++;
- if (n > srclen)
- goto bufferTooSmall;
- if (n == 1 || n > 6)
- goto badCharacter;
- for (j = 1; j < n; j++) {
- if ((src[j] & 0xC0) != 0x80)
- goto badCharacter;
- }
- v = DecodeChar((const uint8 *) src, n);
- if (v >= 0x10000) {
- v -= 0x10000;
- if (v > 0xFFFFF || dstlen < 2) {
- *dstlenp = (origDstlen - dstlen);
- return JS_FALSE;
- }
- if (dstlen < 2)
- goto bufferTooSmall;
- if (dst) {
- *dst++ = (jschar)((v >> 10) + 0xD800);
- v = (jschar)((v & 0x3FF) + 0xDC00);
- }
- dstlen--;
- }
- }
- if (!dstlen)
- goto bufferTooSmall;
- if (dst)
- *dst++ = (jschar) v;
- dstlen--;
- offset += n;
- src += n;
- srclen -= n;
- }
- *dstlenp = (origDstlen - dstlen);
- return JS_TRUE;
-
-badCharacter:
- *dstlenp = (origDstlen - dstlen);
- return JS_FALSE;
-
-bufferTooSmall:
- *dstlenp = (origDstlen - dstlen);
- return JS_FALSE;
-}
-
-static JSBool
-EvalInContext(JSContext *context, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval) {
- JSString *str;
- JSObject *sandbox;
- JSContext *sub_context;
- const jschar *src;
- size_t srclen;
- JSBool ok;
- jsval v;
-
- sandbox = NULL;
- if (!JS_ConvertArguments(context, argc, argv, "S / o", &str, &sandbox))
- return JS_FALSE;
-
- sub_context = JS_NewContext(JS_GetRuntime(context), gStackChunkSize);
- if (!sub_context) {
- JS_ReportOutOfMemory(context);
- return JS_FALSE;
- }
-
-#ifdef USE_JS_SETOPCB
- JS_SetContextThread(sub_context);
- JS_BeginRequest(sub_context);
-#endif
-
- src = JS_GetStringChars(str);
- srclen = JS_GetStringLength(str);
-
- if (!sandbox) {
- sandbox = JS_NewObject(sub_context, NULL, NULL, NULL);
- if (!sandbox || !JS_InitStandardClasses(sub_context, sandbox)) {
- ok = JS_FALSE;
- goto out;
- }
- }
-
- if (srclen == 0) {
- *rval = OBJECT_TO_JSVAL(sandbox);
- ok = JS_TRUE;
- } else {
- ok = JS_EvaluateUCScript(sub_context, sandbox, src, srclen, NULL, 0,
- rval);
- ok = JS_TRUE;
- }
-
-out:
-#ifdef USE_JS_SETOPCB
- JS_EndRequest(sub_context);
- JS_ClearContextThread(sub_context);
-#endif
-
- JS_DestroyContext(sub_context);
- return ok;
-}
-
-static JSBool
-GC(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- JS_GC(context);
- return JS_TRUE;
-}
-
-static JSBool
-Print(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- uintN i;
- size_t cl, bl;
- JSString *str;
- jschar *chars;
- char *bytes;
-
- for (i = 0; i < argc; i++) {
- str = JS_ValueToString(context, argv[i]);
- if (!str)
- return JS_FALSE;
- chars = JS_GetStringChars(str);
- cl = JS_GetStringLength(str);
- if (!EncodeString(chars, cl, NULL, &bl))
- return JS_FALSE;
- bytes = JS_malloc(context, bl + 1);
- bytes[bl] = '\0';
- if (!EncodeString(chars, cl, bytes, &bl)) {
- JS_free(context, bytes);
- return JS_FALSE;
- }
- fprintf(stdout, "%s%s", i ? " " : "", bytes);
- JS_free(context, bytes);
- }
-
- fputc('\n', stdout);
- fflush(stdout);
- return JS_TRUE;
-}
-
-static JSBool
-Quit(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- JS_ConvertArguments(context, argc, argv, "/ i", &gExitCode);
- return JS_FALSE;
-}
-
-static JSBool
-ReadLine(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- char *bytes, *tmp;
- jschar *chars;
- size_t bufsize, byteslen, charslen, readlen;
- JSString *str;
-
- JS_MaybeGC(context);
-
- byteslen = 0;
- bufsize = 256;
- bytes = JS_malloc(context, bufsize);
- if (!bytes)
- return JS_FALSE;
-
- while ((readlen = js_fgets(bytes + byteslen, bufsize - byteslen, stdin)) > 0) {
- byteslen += readlen;
-
- /* Are we done? */
- if (bytes[byteslen - 1] == '\n') {
- bytes[byteslen - 1] = '\0';
- break;
- }
-
- /* Else, grow our buffer for another pass */
- tmp = JS_realloc(context, bytes, bufsize * 2);
- if (!tmp) {
- JS_free(context, bytes);
- return JS_FALSE;
- }
-
- bufsize *= 2;
- bytes = tmp;
- }
-
- /* Treat the empty string specially */
- if (byteslen == 0) {
- *rval = JS_GetEmptyStringValue(context);
- JS_free(context, bytes);
- return JS_TRUE;
- }
-
- /* Shrink the buffer to the real size */
- tmp = JS_realloc(context, bytes, byteslen);
- if (!tmp) {
- JS_free(context, bytes);
- return JS_FALSE;
- }
- bytes = tmp;
-
- /* Decode the string from UTF-8 */
- if (!DecodeString(bytes, byteslen, NULL, &charslen)) {
- JS_free(context, bytes);
- return JS_FALSE;
- }
- chars = JS_malloc(context, (charslen + 1) * sizeof(jschar));
- if (!DecodeString(bytes, byteslen, chars, &charslen)) {
- JS_free(context, bytes);
- JS_free(context, chars);
- return JS_FALSE;
- }
- JS_free(context, bytes);
- chars[charslen] = '\0';
-
- /* Initialize a JSString object */
- str = JS_NewUCString(context, chars, charslen - 1);
- if (!str) {
- JS_free(context, chars);
- return JS_FALSE;
- }
-
- *rval = STRING_TO_JSVAL(str);
- return JS_TRUE;
-}
-
-static JSBool
-Seal(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- JSObject *target;
- JSBool deep = JS_FALSE;
-
- if (!JS_ConvertArguments(context, argc, argv, "o/b", &target, &deep))
- return JS_FALSE;
- if (!target)
- return JS_TRUE;
- return JS_SealObject(context, target, deep);
-}
-
-static void
-ExecuteScript(JSContext *context, JSObject *obj, const char *filename) {
- FILE *file;
- JSScript *script;
- jsval result;
-
- if (!filename || strcmp(filename, "-") == 0) {
- file = stdin;
- } else {
- file = fopen(filename, "r");
- if (!file) {
- fprintf(stderr, "could not open script file %s\n", filename);
- gExitCode = 1;
- return;
- }
- }
-
- script = JS_CompileFileHandle(context, obj, filename, file);
- if (script) {
- JS_ExecuteScript(context, obj, script, &result);
- JS_DestroyScript(context, script);
- }
-}
-
-static uint32 gBranchCount = 0;
-
-#ifdef USE_JS_SETOPCB
-static JSBool
-OperationCallback(JSContext *context)
-{
- if ((++gBranchCount & 0x3fff) == 1) {
- JS_MaybeGC(context);
- }
- return JS_TRUE;
-}
-#else
-static JSBool
-BranchCallback(JSContext *context, JSScript *script) {
- if ((++gBranchCount & 0x3fff) == 1) {
- JS_MaybeGC(context);
- }
- return JS_TRUE;
-}
-#endif
-
-static void
-PrintError(JSContext *context, const char *message, JSErrorReport *report) {
- if (!report || !JSREPORT_IS_WARNING(report->flags))
- fprintf(stderr, "%s\n", message);
-}
-
-JSBool ThrowError(JSContext *cx, const char *message)
-{
- void *mark;
- jsval *args;
- jsval exc;
-
- printf("%s\n",message);
-
- args = JS_PushArguments(cx, &mark, "s", message);
- if (args) {
- if (JS_CallFunctionName(cx, JS_GetGlobalObject(cx),
- "Error", 1, args, &exc))
- JS_SetPendingException(cx, exc);
- JS_PopArguments(cx, mark);
- }
-
- return JS_FALSE;
-}
-
-typedef struct buffer_counter {
- Buffer buffer;
- int pos;
-}* BufferCount;
-
-size_t curl_read(void *ptr, size_t size, size_t nmemb, void *stream) {
- int readlength, spaceleft, i;
- char* databuffer = (char*)ptr;
- Buffer b = ((BufferCount)stream)->buffer;
- int* pos = &(((BufferCount)stream)->pos);
-
- if( size == 0 || nmemb == 0) {
- return 0;
- }
-
- if((b->count - *pos) == 0) {
- return 0;
- }
-
- readlength = size*nmemb;
- spaceleft = b->count - *pos;
-
- if(readlength < spaceleft) {
- copy_Buffer(b,databuffer,*pos,readlength);
- *(pos) += readlength;
- return readlength;
- } else {
- copy_Buffer(b,databuffer,*pos,spaceleft);
- *(pos) += spaceleft;
- return spaceleft;
- }
-}
-
-size_t curl_write(void *ptr, size_t size, size_t nmemb, void *stream) {
- char *data, *tmp;
- Buffer b;
- if( size == 0 || nmemb == 0 )
- return 0;
-
- data = (char *)ptr;
- b = (Buffer)stream;
-
- append_Buffer(b,data,size*nmemb);
-
- return size*nmemb;
-}
-
-// This uses MALLOC dont forget to free
-char* JSValToChar(JSContext* context, jsval* arg) {
- char *c, *tmp;
- JSString *jsmsg;
- size_t len;
- int i;
- if(!JSVAL_IS_STRING(*arg)) {
- return NULL;
- }
-
- jsmsg = JS_ValueToString(context,*arg);
- len = JS_GetStringLength(jsmsg);
- tmp = JS_GetStringBytes(jsmsg);
-
- c = (char*)malloc(len+1);
- c[len] = '\0';
-
- for(i = 0;i < len;i++) {
- c[i] = tmp[i];
- }
-
- return c;
-}
-
-JSBool BufferToJSVal(JSContext *context, Buffer b, jsval *rval) {
- char* c;
- JSString *str;
-
- // Important for char* to be JS_malloced, otherwise js wont let you use it in the NewString method
- c = JS_malloc(context, b->count * sizeof(char));
- copy_Buffer(b,c,0,b->count);
-
-
- /* Initialize a JSString object */
- str = JS_NewString(context, c, b->count);
-
- if (!str) {
- JS_free(context, c);
- return JS_FALSE;
- }
-
- // Set Return Value
- *rval = STRING_TO_JSVAL(str);
- if(rval == NULL) {
- return JS_FALSE;
- }
- return JS_TRUE;
-}
-
-struct curl_slist* generateCurlHeaders(JSContext* context,jsval* arg) {
- // If arg is an object then we go the header-hash route else return NULL
-
- if(!JSVAL_IS_NULL(*arg)) {
-
- struct curl_slist *slist = NULL;
- JSObject* header_obj;
- JSObject* iterator;
- jsval *jsProperty;
- jsval *jsValue;
- jsid *jsId;
- Buffer bTmp;
- char* jsPropertyName, *jsPropertyValue;
-
- // If we fail to convert arg2 to an object. Error!
- if(!JS_ValueToObject(context,*arg,&header_obj)) {
- return NULL;
- }
-
- iterator = JS_NewPropertyIterator(context,header_obj);
-
- jsProperty = JS_malloc(context,sizeof(jsval));
- jsValue = JS_malloc(context,sizeof(jsval));
- jsId = JS_malloc(context,sizeof(jsid));
-
- while(JS_NextProperty(context,iterator,jsId) == JS_TRUE) {
-
- if(*jsId == JSVAL_VOID) {
- break;
- }
-
- // TODO: Refactor this maybe make a JSValAppendBuffer method b/c that is what you really want to do.
-
- bTmp = init_Buffer();
- JS_IdToValue(context,*jsId,jsProperty);
- jsPropertyName = JSValToChar(context,jsProperty);
-
- // TODO: Remove strlen =/
- append_Buffer(bTmp,jsPropertyName,strlen(jsPropertyName));
- append_Buffer(bTmp,": ",2);
-
- JS_GetProperty(context,header_obj,jsPropertyName,jsValue);
- jsPropertyValue = JSValToChar(context,jsValue);
- // TODO: Remove strlen =/
- append_Buffer(bTmp,jsPropertyValue,strlen(jsPropertyValue));
- append_Buffer(bTmp,"",1);
-
- slist = curl_slist_append(slist,bTmp->data);
-
- free_Buffer(bTmp);
- free(jsPropertyValue);
- free(jsPropertyName);
- }
-
- JS_free(context,jsProperty);
- JS_free(context,jsValue);
- JS_free(context,jsId);
-
- return slist;
-
- } else {
- return NULL;
- }
-}
-
-static JSBool
-GetHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- CURL* handle;
- Buffer b;
- char *url;
- size_t charslen, readlen;
- struct curl_slist *slist;
- int exitcode;
-
- // Run GC
- JS_MaybeGC(context);
-
- // Init Curl
- if((handle = curl_easy_init()) == NULL) {
- return JS_FALSE;
- }
-
- // Get URL
- url = JSValToChar(context,argv);
- if( url == NULL ) {
- return ThrowError(context,"Unable to convert url (argument 0) to a string");
- }
-
- b = init_Buffer(); // Allocate buffer that will store the get resultant
-
- // Configuration
- curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
- curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
- curl_easy_setopt(handle,CURLOPT_URL,url);
- curl_easy_setopt(handle,CURLOPT_HTTPGET,1);
- curl_easy_setopt(handle,CURLOPT_FOLLOWLOCATION,1);
- curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
- curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
-
- slist = generateCurlHeaders(context,argv+1);
- if(slist != NULL) {
- curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
- }
-
- // Perform
- if((exitcode = curl_easy_perform(handle)) != 0) {
- if(slist != NULL) {
- curl_slist_free_all(slist);
- }
- curl_easy_cleanup(handle);
- free(url);
- free_Buffer(b);
- return JS_FALSE;
- }
-
- free(url);
- if(slist != NULL) {
- curl_slist_free_all(slist);
- }
-
- /* Treat the empty string specially */
- if (b->count == 0) {
- free_Buffer(b);
- *rval = JS_GetEmptyStringValue(context);
- curl_easy_cleanup(handle);
- return JS_TRUE;
- }
-
- /* Shrink the buffer to the real size and store its value in rval */
- shrink_Buffer(b);
- BufferToJSVal(context,b,rval);
-
- // Free Buffer
- free_Buffer(b);
-
- if(rval == NULL) {
- curl_easy_cleanup(handle);
- return JS_FALSE;
- }
-
- JS_MaybeGC(context);
-
- curl_easy_cleanup(handle);
-
- return JS_TRUE;
-}
-
-static JSBool
-HeadHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- CURL* handle;
- Buffer b;
- char *url;
- size_t charslen, readlen;
- struct curl_slist *slist;
- int exitcode;
-
- // Run GC
- JS_MaybeGC(context);
-
- // Init Curl
- if((handle = curl_easy_init()) == NULL) {
- return JS_FALSE;
- }
-
- // Get URL
- url = JSValToChar(context,argv);
- if( url == NULL ) {
- return ThrowError(context,"Unable to convert url (argument 0) to a string");
- }
-
- b = init_Buffer(); // Allocate buffer that will store the get resultant
-
- // Configuration
- // curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
- // curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
- curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
- curl_easy_setopt(handle,CURLOPT_URL,url);
- curl_easy_setopt(handle,CURLOPT_HTTPGET,0);
- curl_easy_setopt(handle,CURLOPT_NOBODY,1);
- curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
- curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
-
- slist = generateCurlHeaders(context,argv+1);
- if(slist != NULL) {
- curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
- }
-
- // fprintf(stderr, "about to run HEAD request\n");
-
- // Perform
- if((exitcode = curl_easy_perform(handle)) != 0) {
- if(slist != NULL) {
- curl_slist_free_all(slist);
- }
- curl_easy_cleanup(handle);
- free(url);
- free_Buffer(b);
- return JS_FALSE;
- }
- // fprintf(stderr, "ran ok HEAD request\n");
-
- free(url);
- if(slist != NULL) {
- curl_slist_free_all(slist);
- }
-
- /* Treat the empty string specially */
- if (b->count == 0) {
- free_Buffer(b);
- *rval = JS_GetEmptyStringValue(context);
- curl_easy_cleanup(handle);
- return JS_TRUE;
- }
-
- /* Shrink the buffer to the real size and store its value in rval */
- shrink_Buffer(b);
- BufferToJSVal(context,b,rval);
-
- // Free Buffer
- free_Buffer(b);
-
- if(rval == NULL) {
- curl_easy_cleanup(handle);
- return JS_FALSE;
- }
-
- JS_MaybeGC(context);
-
- curl_easy_cleanup(handle);
-
- return JS_TRUE;
-}
-
-
-static JSBool
-PostHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- CURL* handle;
- Buffer b;
- char *url, *body;
- size_t charslen, readlen;
- struct curl_slist *slist;
- int exitcode;
-
- // Run GC
- JS_MaybeGC(context);
-
- // Init Curl
- if((handle = curl_easy_init()) == NULL) {
- return JS_FALSE;
- }
-
- // Get URL
- if((url = JSValToChar(context,argv)) == NULL) {
- curl_easy_cleanup(handle);
- return JS_FALSE;
- }
-
- // Initialize buffer
- b = init_Buffer();
-
- curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write); // function that recieves data
- curl_easy_setopt(handle,CURLOPT_WRITEDATA,b); // buffer to write the data to
- curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
- curl_easy_setopt(handle,CURLOPT_URL,url); // url
- curl_easy_setopt(handle,CURLOPT_POST,1); // Set Op. to post
- curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1); // No Progress Meter
- curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4); // only ipv4
-
- if((body = JSValToChar(context,argv+1)) == NULL) { // Convert arg1 to a string
- free(url);
- free_Buffer(b);
- curl_easy_cleanup(handle);
- return JS_FALSE;
- }
-
- curl_easy_setopt(handle,CURLOPT_POSTFIELDSIZE,strlen(body));
- curl_easy_setopt(handle,CURLOPT_POSTFIELDS,body); // Curl wants '\0' terminated, we oblige
-
- slist = generateCurlHeaders(context,argv+2); // Initialize Headers
- if(slist != NULL) {
- curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
- }
-
- if((exitcode = curl_easy_perform(handle)) != 0) { // Perform
- curl_slist_free_all(slist);
- free(body);
- free(url);
- free_Buffer(b);
- curl_easy_cleanup(handle);
- return JS_FALSE;
- }
-
- free(body);
- free(url);
- curl_slist_free_all(slist);
-
- // Convert response back to javascript value and then clean
- BufferToJSVal(context,b,rval);
- free_Buffer(b);
- curl_easy_cleanup(handle);
-
- JS_MaybeGC(context);
-
- if( rval == NULL ) {
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-#define CLEAN \
- free_Buffer(b); \
- free_Buffer(b_data->buffer); \
- free(b_data); \
- free(url)
-
-static JSBool
-PutHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
-
- Buffer b;
- BufferCount b_data;
- char *url, *data;
- size_t charslen, readlen;
- JSObject* header_obj;
- CURL* handle;
- struct curl_slist *slist;
- int exitcode;
-
- // Run GC
- JS_MaybeGC(context);
-
- // Get URL
- url = JSValToChar(context,argv);
-
- // Allocate buffer that will store the get resultant
- b = init_Buffer();
-
- // Allocate data buffer and move data into them
- b_data = (BufferCount)malloc(sizeof(Buffer) + sizeof(int));
- b_data->buffer = init_Buffer();
- b_data->pos = 0;
-
- data = JSValToChar(context,(argv+1));
- readlen = strlen(data);
-
-
-
- // TODO: remove strlen
- append_Buffer(b_data->buffer,data,readlen);
-
- free(data);
-
- // Init Curl
-
- if((handle = curl_easy_init()) == NULL) {
- CLEAN;
- return JS_FALSE;
- }
-
- // Configuration
- curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
- curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
- curl_easy_setopt(handle,CURLOPT_READFUNCTION,curl_read);
- curl_easy_setopt(handle,CURLOPT_READDATA,b_data);
- curl_easy_setopt(handle,CURLOPT_URL,url);
- curl_easy_setopt(handle,CURLOPT_UPLOAD,1);
- curl_easy_setopt(handle,CURLOPT_INFILESIZE,readlen);
-
-
-
- // Curl structure
- slist = generateCurlHeaders(context,argv+2);
- if(slist != NULL) {
- curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
- }
-
- // Little Things
- // No progress meter
- curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
- // Use only ipv4
- curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
-
-
-
- // Perform
- if((exitcode = curl_easy_perform(handle)) != 0) {
- if(slist != NULL)
- curl_slist_free_all(slist);
- curl_easy_cleanup(handle);
- CLEAN;
- return JS_FALSE;
- }
-
- if(slist != NULL)
- curl_slist_free_all(slist);
- free_Buffer(b_data->buffer);
- free(b_data);
- free(url);
-
- /* Treat the empty string specially */
- if (b->count == 0) {
- *rval = JS_GetEmptyStringValue(context);
- curl_easy_cleanup(handle);
- free_Buffer(b);
- return JS_TRUE;
- }
-
- /* Shrink the buffer to the real size */
- shrink_Buffer(b);
-
- BufferToJSVal(context,b,rval);
-
- free_Buffer(b);
-
- if(rval == NULL) {
- curl_easy_cleanup(handle);
- return JS_FALSE;
- }
-
- JS_MaybeGC(context);
-
- curl_easy_cleanup(handle);
-
- return JS_TRUE;
-}
-
-static JSBool
-DelHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- Buffer b;
- char *url;
- size_t charslen, readlen;
- char header_name[7];
- CURL* handle;
- int exitcode;
- struct curl_slist *slist = NULL;
-
- strcpy(header_name,"DELETE");
-
- // Run GC
- JS_MaybeGC(context);
-
- // Get URL
- url = JSValToChar(context,argv);
-
- // Allocate buffer that will store the del resultant
- b = init_Buffer();
-
- // Init Curl
- if((handle = curl_easy_init()) == NULL) {
- free_Buffer(b);
- return JS_FALSE;
- }
-
- // Configuration
- curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
- curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
- curl_easy_setopt(handle,CURLOPT_URL,url);
- curl_easy_setopt(handle,CURLOPT_CUSTOMREQUEST,header_name);
- curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
- curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
-
- // Curl structure
- if((slist = generateCurlHeaders(context,argv+1)) != NULL) {
- curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
- }
-
- // Perform
- if((exitcode = curl_easy_perform(handle)) != 0) {
- if(slist != NULL)
- curl_slist_free_all(slist);
- curl_easy_cleanup(handle);
- free(url);
- free_Buffer(b);
- return JS_FALSE;
- }
-
- if(slist != NULL)
- curl_slist_free_all(slist);
- free(url);
-
- /* Treat the empty string specially */
- if (b->count == 0) {
- *rval = JS_GetEmptyStringValue(context);
- curl_easy_cleanup(handle);
- free_Buffer(b);
- return JS_TRUE;
- }
-
- /* Shrink the buffer to the real size */
- shrink_Buffer(b);
-
- BufferToJSVal(context,b,rval);
-
- if(rval == NULL) {
- curl_easy_cleanup(handle);
- return JS_FALSE;
- }
-
- JS_MaybeGC(context);
-
- curl_easy_cleanup(handle);
-
- return JS_TRUE;
-}
-
-static JSBool
-CopyHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- Buffer b;
- char *url;
- size_t charslen, readlen;
- char header_name[5];
- CURL* handle;
- int exitcode;
- struct curl_slist *slist = NULL;
-
- strcpy(header_name,"COPY");
-
- // Run GC
- JS_MaybeGC(context);
-
- // Get URL
- url = JSValToChar(context,argv);
-
- // Allocate buffer that will store the del resultant
- b = init_Buffer();
-
- // Init Curl
- if((handle = curl_easy_init()) == NULL) {
- free_Buffer(b);
- return JS_FALSE;
- }
-
- // Configuration
- curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
- curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
- curl_easy_setopt(handle,CURLOPT_URL,url);
- curl_easy_setopt(handle,CURLOPT_CUSTOMREQUEST,header_name);
- curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
- curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
-
- // Curl structure
- if((slist = generateCurlHeaders(context,argv+1)) != NULL) {
- curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
- }
-
- // Perform
- if((exitcode = curl_easy_perform(handle)) != 0) {
- if(slist != NULL)
- curl_slist_free_all(slist);
- curl_easy_cleanup(handle);
- free(url);
- free_Buffer(b);
- return JS_FALSE;
- }
-
- if(slist != NULL)
- curl_slist_free_all(slist);
- free(url);
-
- /* Treat the empty string specially */
- if (b->count == 0) {
- *rval = JS_GetEmptyStringValue(context);
- curl_easy_cleanup(handle);
- free_Buffer(b);
- return JS_TRUE;
- }
-
- /* Shrink the buffer to the real size */
- shrink_Buffer(b);
-
- BufferToJSVal(context,b,rval);
-
- if(rval == NULL) {
- curl_easy_cleanup(handle);
- return JS_FALSE;
- }
-
- JS_MaybeGC(context);
-
- curl_easy_cleanup(handle);
-
- return JS_TRUE;
-}
-
-static JSBool
-MoveHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- Buffer b;
- char *url;
- size_t charslen, readlen;
- char header_name[5];
- CURL* handle;
- struct curl_slist *slist = NULL;
- int exitcode;
-
- strcpy(header_name,"MOVE");
-
- // Run GC
- JS_MaybeGC(context);
-
- // Get URL
- url = JSValToChar(context,argv);
-
- // Allocate buffer that will store the del resultant
- b = init_Buffer();
-
- // Init Curl
- if((handle = curl_easy_init()) == NULL) {
- free_Buffer(b);
- return JS_FALSE;
- }
-
- // Configuration
- curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
- curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
- curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
- curl_easy_setopt(handle,CURLOPT_URL,url);
- curl_easy_setopt(handle,CURLOPT_CUSTOMREQUEST,header_name);
- curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
- curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
-
- // Curl structure
- if((slist = generateCurlHeaders(context,argv+1)) != NULL) {
- curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
- }
-
- // Perform
- if((exitcode = curl_easy_perform(handle)) != 0) {
- if(slist != NULL)
- curl_slist_free_all(slist);
- curl_easy_cleanup(handle);
- free(url);
- free_Buffer(b);
- return JS_FALSE;
- }
-
- if(slist != NULL)
- curl_slist_free_all(slist);
- free(url);
-
- /* Treat the empty string specially */
- if (b->count == 0) {
- *rval = JS_GetEmptyStringValue(context);
- curl_easy_cleanup(handle);
- free_Buffer(b);
- return JS_TRUE;
- }
-
- /* Shrink the buffer to the real size */
- shrink_Buffer(b);
-
- BufferToJSVal(context,b,rval);
-
- if(rval == NULL) {
- curl_easy_cleanup(handle);
- return JS_FALSE;
- }
-
- JS_MaybeGC(context);
-
- curl_easy_cleanup(handle);
-
- return JS_TRUE;
-}
-
-int
-main(int argc, const char * argv[]) {
- JSRuntime *runtime;
- JSContext *context;
- JSObject *global;
-
- runtime = JS_NewRuntime(64L * 1024L * 1024L);
- if (!runtime)
- return 1;
- context = JS_NewContext(runtime, gStackChunkSize);
- if (!context)
- return 1;
- /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=477187 */
- JS_SetErrorReporter(context, PrintError);
-#ifdef USE_JS_SETOPCB
- JS_SetContextThread(context);
- JS_BeginRequest(context);
- JS_SetOperationCallback(context, OperationCallback);
-#else
- JS_SetBranchCallback(context, BranchCallback);
- JS_ToggleOptions(context, JSOPTION_NATIVE_BRANCH_CALLBACK);
-#endif
- JS_ToggleOptions(context, JSOPTION_XML);
-
- global = JS_NewObject(context, NULL, NULL, NULL);
- if (!global)
- return 1;
- if (!JS_InitStandardClasses(context, global))
- return 1;
- if (!JS_DefineFunction(context, global, "evalcx", EvalInContext, 0, 0)
- || !JS_DefineFunction(context, global, "gc", GC, 0, 0)
- || !JS_DefineFunction(context, global, "print", Print, 0, 0)
- || !JS_DefineFunction(context, global, "quit", Quit, 0, 0)
- || !JS_DefineFunction(context, global, "readline", ReadLine, 0, 0)
- || !JS_DefineFunction(context, global, "seal", Seal, 0, 0)
- || !JS_DefineFunction(context, global, "gethttp", GetHttp, 1, 0)
- || !JS_DefineFunction(context, global, "headhttp", HeadHttp, 1, 0)
- || !JS_DefineFunction(context, global, "posthttp", PostHttp, 2, 0)
- || !JS_DefineFunction(context, global, "puthttp", PutHttp, 2, 0)
- || !JS_DefineFunction(context, global, "delhttp", DelHttp, 1, 0)
- || !JS_DefineFunction(context, global, "movehttp", MoveHttp, 1, 0)
- || !JS_DefineFunction(context, global, "copyhttp", CopyHttp, 1, 0))
- return 1;
-
- if (argc != 2) {
- fprintf(stderr, "incorrect number of arguments\n\n");
- fprintf(stderr, "usage: %s <scriptfile>\n", argv[0]);
- return 2;
- }
-
- ExecuteScript(context, global, argv[1]);
-
-#ifdef USE_JS_SETOPCB
- JS_EndRequest(context);
- JS_ClearContextThread(context);
-#endif
-
- JS_DestroyContext(context);
- JS_DestroyRuntime(runtime);
- JS_ShutDown();
-
- return gExitCode;
-}
diff --git a/src/couchdb/priv/couch_js/curlhelper.c b/src/couchdb/priv/couch_js/curlhelper.c
deleted file mode 100644
index 738ac64a..00000000
--- a/src/couchdb/priv/couch_js/curlhelper.c
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use
-this file except in compliance with the License. You may obtain a copy of the
-License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software distributed
-under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
-CONDITIONS OF ANY KIND, either express or implied. See the License for the
-specific language governing permissions and limitations under the License.
-
-*/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include "curlhelper.h"
-
-#define TRUE 1
-#define FALSE 0
-
-Buffer init_Buffer() {
- Buffer b;
-
- if((b = (Buffer)malloc(sizeof(char*) + sizeof(int)*2)) == NULL) {
- return NULL;
- }
-
- b->count = 0;
- b->capacity = 50;
-
- if(b->data = (char*)malloc(sizeof(char)*b->capacity)) {
- return b;
- } else {
- return NULL;
- }
-}
-
-void free_Buffer(Buffer b) {
- if(b == NULL)
- return;
- if(b->data != NULL)
- free(b->data);
- free(b);
-}
-
-int append_Buffer(Buffer b, char* c, int length) {
- int capacity_changed;
- int new_count;
- int i;
-
- capacity_changed = FALSE;
- new_count = b->count + length;
-
- if(new_count > b->capacity) {
- capacity_changed = TRUE;
- b->capacity *= 2;
- }
-
- while(new_count > b->capacity) {
- b->capacity *= 2;
- }
-
- if(capacity_changed) {
- if((b->data = (char*)realloc(b->data,b->capacity)) == NULL) {
- return FALSE;
- }
- }
-
- for(i = 0;i < length;i++) {
- *(b->data + b->count + i) = *(c + i);
- }
-
- b->count = new_count;
-
- return TRUE;
-}
-
-char* toString_Buffer(Buffer b) {
- char* result;
- int i;
-
- if((result = (char*)malloc(sizeof(char)*(b->count+1))) == NULL) {
- return NULL;
- }
-
- result[b->count] = '\0';
-
- for(i = 0;i < b->count;i++) {
- result[i] = b->data[i];
- }
-
- return result;
-}
-
-int print_Buffer(Buffer b) {
- char* c;
-
- if((c = toString_Buffer(b)) == NULL) {
- return FALSE;
- }
-
- printf("%s\n",c);
-
- free(c);
-
- return TRUE;
-}
-
-
-int shrink_Buffer(Buffer b) {
- b->capacity = b->count;
-
- if((b->data = realloc(b->data,sizeof(char)*b->capacity)) == NULL) {
- return FALSE;
- } else {
- return TRUE;
- }
-}
-
-void copy_Buffer(Buffer b, char* c, int pos, int length) {
- int i;
- if((pos + length) > b->count)
- return;
-
- for(i = 0; i < length;i++) {
- *(c + i) = *(b->data + pos + i);
- }
-}
-
-
-List init_List(int capacity) {
- List l;
- if(capacity < 5)
- capacity = 5;
-
- if((l = (List)malloc(sizeof(void**)+sizeof(int)*2)) == NULL) {
- return NULL;
- }
-
- l->count = 0;
- l->capacity = capacity;
-
- if((l->elements = (void**)malloc(sizeof(void*)*l->capacity)) == NULL) {
- return NULL;
- }
-
- return l;
-}
-
-void free_List(List l) {
- if(l == NULL)
- return;
- if(l->elements != NULL)
- free(l->elements);
- free(l);
-}
-
-void* get_List(List l, int pos) {
- if(pos > (l->count - 1)) {
- return NULL;
- }
- return *(l->elements + pos);
-}
-
-void* pull_List(List l) {
- void* r = *(l->elements);
-
- int i;
-
- for(i = 1; i < (l->count-1);i++) {
- l->elements[i] = l->elements[i+1];
- }
- l->count -= 1;
- return r;
-}
-
-int set_List(List l, int pos, void* ptr) {
- if(pos > (l->count - 1)) {
- return FALSE;
- }
-
- *(l->elements + pos) = ptr;
-
- return TRUE;
-}
-
-int append_List(List l, void* ptr, int length) {
- int capacity_changed;
- int new_count;
- int i;
-
- capacity_changed = FALSE;
- new_count = l->count + length;
-
- if(new_count > l->capacity) {
- capacity_changed = TRUE;
- l->capacity *= 2;
- }
-
- while(new_count > l->capacity) {
- l->capacity *= 2;
- }
-
- if(capacity_changed) {
- if((l->elements = (void*)realloc(l->elements,l->capacity)) == NULL) {
- return FALSE;
- }
- }
-
- for(i = 0;i < length;i++) {
- *(l->elements + l->count + i) = (void *)((char *)ptr + i);
- }
-
- l->count = new_count;
-
- return TRUE;
-}
-
-int push_List(List l, void* ptr, int length) {
- int capacity_changed;
- int new_count;
- int i;
-
- capacity_changed = FALSE;
- new_count = l->count + length;
-
- if(new_count > l->capacity) {
- capacity_changed = TRUE;
- l->capacity *= 2;
- }
-
- while(new_count > l->capacity) {
- l->capacity *= 2;
- }
-
- if(capacity_changed) {
- if((l->elements = (void*)realloc(l->elements,l->capacity)) == NULL) {
- return FALSE;
- }
- }
-
- for(i = 0;i < length;i++) {
- *(l->elements + l->count + i) = *(l->elements + i);
- }
-
- for(i = 0;i < length;i++) {
- *(l->elements + i) = (void *)((char *)ptr+i);
- }
-
- l->count = new_count;
-
- return TRUE;
-}
diff --git a/src/couchdb/priv/couch_js/curlhelper.h b/src/couchdb/priv/couch_js/curlhelper.h
deleted file mode 100644
index 098bdf02..00000000
--- a/src/couchdb/priv/couch_js/curlhelper.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use
-this file except in compliance with the License. You may obtain a copy of the
-License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software distributed
-under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
-CONDITIONS OF ANY KIND, either express or implied. See the License for the
-specific language governing permissions and limitations under the License.
-
-*/
-
-#ifndef CURLHELPER_H
-#define CURLHELPER_H
-
-typedef struct {
- char* data;
- int count;
- int capacity;
-}* Buffer;
-
-Buffer init_Buffer();
-void free_Buffer(Buffer b);
-int append_Buffer(Buffer b,char* c,int length);
-// WARNING USES MALLOC DONT FORGET TO FREE
-char* toString_Buffer(Buffer b);
-int print_Buffer(Buffer b);
-int shrink_Buffer(Buffer b);
-void copy_Buffer(Buffer b, char* c, int pos, int length);
-
-
-typedef struct {
- void** elements;
- int count;
- int capacity;
-}* List;
-
-List init_List(int capacity);
-void free_List(List l);
-void* get_List(List l, int pos);
-void* pull_List(List l);
-int set_List(List l, int pos, void* ptr);
-int append_List(List l, void* ptr, int length);
-int push_List(List l, void* ptr, int length);
-
-#endif
diff --git a/src/couchdb/priv/couch_js/http.c b/src/couchdb/priv/couch_js/http.c
new file mode 100644
index 00000000..6ee337af
--- /dev/null
+++ b/src/couchdb/priv/couch_js/http.c
@@ -0,0 +1,647 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <stdlib.h>
+#include <string.h>
+#include <jsapi.h>
+#include <curl/curl.h>
+
+#include "utf8.h"
+
+typedef struct curl_slist CurlHeaders;
+
+typedef struct {
+ int method;
+ char* url;
+ CurlHeaders* req_headers;
+ jsint last_status;
+} HTTPData;
+
+char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL};
+
+#define GET 0
+#define HEAD 1
+#define POST 2
+#define PUT 3
+#define DELETE 4
+#define COPY 5
+
+static JSBool
+go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t blen);
+
+static JSBool
+constructor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ HTTPData* http = NULL;
+ JSBool ret = JS_FALSE;
+
+ http = (HTTPData*) malloc(sizeof(HTTPData));
+ if(!http)
+ {
+ JS_ReportError(cx, "Failed to create CouchHTTP instance.");
+ goto error;
+ }
+
+ http->method = -1;
+ http->url = NULL;
+ http->req_headers = NULL;
+ http->last_status = -1;
+
+ if(!JS_SetPrivate(cx, obj, http))
+ {
+ JS_ReportError(cx, "Failed to set private CouchHTTP data.");
+ goto error;
+ }
+
+ ret = JS_TRUE;
+ goto success;
+
+error:
+ if(http) free(http);
+
+success:
+ return ret;
+}
+
+static void
+destructor(JSContext* cx, JSObject* obj)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+ if(!http)
+ {
+ fprintf(stderr, "Unable to destroy invalid CouchHTTP instance.\n");
+ }
+ else
+ {
+ if(http->url) free(http->url);
+ if(http->req_headers) curl_slist_free_all(http->req_headers);
+ free(http);
+ }
+}
+
+static JSBool
+open(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+ char* method = NULL;
+ char* url = NULL;
+ JSBool ret = JS_FALSE;
+ int methid;
+
+ if(!http)
+ {
+ JS_ReportError(cx, "Invalid CouchHTTP instance.");
+ goto done;
+ }
+
+ if(argv[0] == JSVAL_VOID)
+ {
+ JS_ReportError(cx, "You must specify a method.");
+ goto done;
+ }
+
+ method = enc_string(cx, argv[0], NULL);
+ if(!method)
+ {
+ JS_ReportError(cx, "Failed to encode method.");
+ goto done;
+ }
+
+ for(methid = 0; METHODS[methid] != NULL; methid++)
+ {
+ if(strcasecmp(METHODS[methid], method) == 0) break;
+ }
+
+ if(methid > COPY)
+ {
+ JS_ReportError(cx, "Invalid method specified.");
+ goto done;
+ }
+
+ http->method = methid;
+
+ if(argv[1] == JSVAL_VOID)
+ {
+ JS_ReportError(cx, "You must specify a URL.");
+ goto done;
+ }
+
+ if(http->url)
+ {
+ free(http->url);
+ http->url = NULL;
+ }
+
+ http->url = enc_string(cx, argv[1], NULL);
+ if(!http->url)
+ {
+ JS_ReportError(cx, "Failed to encode URL.");
+ goto done;
+ }
+
+ if(argv[2] != JSVAL_VOID && argv[2] != JSVAL_FALSE)
+ {
+ JS_ReportError(cx, "Synchronous flag must be false if specified.");
+ goto done;
+ }
+
+ if(http->req_headers)
+ {
+ curl_slist_free_all(http->req_headers);
+ http->req_headers = NULL;
+ }
+
+ // Disable Expect: 100-continue
+ http->req_headers = curl_slist_append(http->req_headers, "Expect:");
+
+ ret = JS_TRUE;
+
+done:
+ if(method) free(method);
+ return ret;
+}
+
+static JSBool
+setheader(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+ char* keystr = NULL;
+ char* valstr = NULL;
+ char* hdrbuf = NULL;
+ size_t hdrlen = -1;
+ JSBool ret = JS_FALSE;
+
+ if(!http)
+ {
+ JS_ReportError(cx, "Invalid CouchHTTP instance.");
+ goto done;
+ }
+
+ if(argv[0] == JSVAL_VOID)
+ {
+ JS_ReportError(cx, "You must speciy a header name.");
+ goto done;
+ }
+
+ keystr = enc_string(cx, argv[0], NULL);
+ if(!keystr)
+ {
+ JS_ReportError(cx, "Failed to encode header name.");
+ goto done;
+ }
+
+ if(argv[1] == JSVAL_VOID)
+ {
+ JS_ReportError(cx, "You must specify a header value.");
+ goto done;
+ }
+
+ valstr = enc_string(cx, argv[1], NULL);
+ if(!valstr)
+ {
+ JS_ReportError(cx, "Failed to encode header value.");
+ goto done;
+ }
+
+ hdrlen = strlen(keystr) + strlen(valstr) + 3;
+ hdrbuf = (char*) malloc(hdrlen * sizeof(char));
+ if(!hdrbuf)
+ {
+ JS_ReportError(cx, "Failed to allocate header buffer.");
+ goto done;
+ }
+
+ snprintf(hdrbuf, hdrlen, "%s: %s", keystr, valstr);
+ http->req_headers = curl_slist_append(http->req_headers, hdrbuf);
+
+ ret = JS_TRUE;
+
+done:
+ if(keystr) free(keystr);
+ if(valstr) free(valstr);
+ if(hdrbuf) free(hdrbuf);
+
+ return ret;
+}
+
+static JSBool
+sendreq(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+ char* body = NULL;
+ size_t bodylen = 0;
+ JSBool ret = JS_FALSE;
+
+ if(!http)
+ {
+ JS_ReportError(cx, "Invalid CouchHTTP instance.");
+ goto done;
+ }
+
+ if(argv[0] != JSVAL_VOID && argv[0] != JS_GetEmptyStringValue(cx))
+ {
+ body = enc_string(cx, argv[0], &bodylen);
+ if(!body)
+ {
+ JS_ReportError(cx, "Failed to encode body.");
+ goto done;
+ }
+ }
+
+ ret = go(cx, obj, http, body, bodylen);
+
+done:
+ if(body) free(body);
+ return ret;
+}
+
+static JSBool
+status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+
+ if(!http)
+ {
+ JS_ReportError(cx, "Invalid CouchHTTP instance.");
+ return JS_FALSE;
+ }
+
+ if(INT_FITS_IN_JSVAL(http->last_status))
+ {
+ *vp = INT_TO_JSVAL(http->last_status);
+ return JS_TRUE;
+ }
+ else
+ {
+ JS_ReportError(cx, "INTERNAL: Invalid last_status");
+ return JS_FALSE;
+ }
+}
+
+JSClass CouchHTTPClass = {
+ "CouchHTTP",
+ JSCLASS_HAS_PRIVATE
+ | JSCLASS_CONSTRUCT_PROTOTYPE
+ | JSCLASS_HAS_RESERVED_SLOTS(2),
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ destructor,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSPropertySpec CouchHTTPProperties[] = {
+ {"status", 0, JSPROP_READONLY, status, NULL},
+ {0, 0, 0, 0, 0}
+};
+
+JSFunctionSpec CouchHTTPFunctions[] = {
+ {"_open", open, 3, 0, 0},
+ {"_setRequestHeader", setheader, 2, 0, 0},
+ {"_send", sendreq, 1, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+JSObject*
+install_http(JSContext* cx, JSObject* glbl)
+{
+ JSObject* klass = NULL;
+ HTTPData* http = NULL;
+
+ klass = JS_InitClass(
+ cx,
+ glbl,
+ NULL,
+ &CouchHTTPClass,
+ constructor,
+ 0,
+ CouchHTTPProperties,
+ CouchHTTPFunctions,
+ NULL,
+ NULL
+ );
+
+ if(!klass)
+ {
+ fprintf(stderr, "Failed to initialize CouchHTTP class.\n");
+ return NULL;
+ }
+
+ return klass;
+}
+
+
+// Curl Helpers
+
+typedef struct {
+ HTTPData* http;
+ JSContext* cx;
+ JSObject* resp_headers;
+ char* sendbuf;
+ size_t sendlen;
+ size_t sent;
+ char* recvbuf;
+ size_t recvlen;
+ size_t read;
+} CurlState;
+
+/*
+ * I really hate doing this but this doesn't have to be
+ * uber awesome, it just has to work.
+ */
+CURL* HANDLE = NULL;
+char ERRBUF[CURL_ERROR_SIZE];
+
+static size_t send_body(void *ptr, size_t size, size_t nmem, void *data);
+static int seek_body(void *ptr, curl_off_t offset, int origin);
+static size_t recv_body(void *ptr, size_t size, size_t nmem, void *data);
+static size_t recv_header(void *ptr, size_t size, size_t nmem, void *data);
+
+static JSBool
+go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
+{
+ CurlState state;
+ JSString* jsbody;
+ JSBool ret = JS_FALSE;
+ jsval tmp;
+
+ state.cx = cx;
+ state.http = http;
+
+ state.sendbuf = body;
+ state.sendlen = bodylen;
+ state.sent = 0;
+
+ state.recvbuf = NULL;
+ state.recvlen = 0;
+ state.read = 0;
+
+ if(HANDLE == NULL)
+ {
+ HANDLE = curl_easy_init();
+ curl_easy_setopt(HANDLE, CURLOPT_READFUNCTION, send_body);
+ curl_easy_setopt(HANDLE, CURLOPT_SEEKFUNCTION, seek_body);
+ curl_easy_setopt(HANDLE, CURLOPT_HEADERFUNCTION, recv_header);
+ curl_easy_setopt(HANDLE, CURLOPT_WRITEFUNCTION, recv_body);
+ curl_easy_setopt(HANDLE, CURLOPT_NOPROGRESS, 1);
+ curl_easy_setopt(HANDLE, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+ curl_easy_setopt(HANDLE, CURLOPT_ERRORBUFFER, ERRBUF);
+ curl_easy_setopt(HANDLE, CURLOPT_COOKIEFILE, "");
+ curl_easy_setopt(HANDLE, CURLOPT_USERAGENT, "CouchHTTP Client - Relax");
+ }
+
+ if(!HANDLE)
+ {
+ JS_ReportError(cx, "Failed to initialize cURL handle.");
+ goto error;
+ }
+
+ if(http->method < 0 || http->method > COPY)
+ {
+ JS_ReportError(cx, "INTERNAL: Unknown method.");
+ goto error;
+ }
+
+ curl_easy_setopt(HANDLE, CURLOPT_CUSTOMREQUEST, METHODS[http->method]);
+ curl_easy_setopt(HANDLE, CURLOPT_NOBODY, 0);
+ curl_easy_setopt(HANDLE, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(HANDLE, CURLOPT_UPLOAD, 0);
+
+ if(http->method == HEAD)
+ {
+ curl_easy_setopt(HANDLE, CURLOPT_NOBODY, 1);
+ curl_easy_setopt(HANDLE, CURLOPT_FOLLOWLOCATION, 0);
+ }
+ else if(http->method == POST || http->method == PUT)
+ {
+ curl_easy_setopt(HANDLE, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(HANDLE, CURLOPT_FOLLOWLOCATION, 0);
+ }
+
+ if(body && bodylen)
+ {
+ curl_easy_setopt(HANDLE, CURLOPT_INFILESIZE, bodylen);
+ }
+ else
+ {
+ curl_easy_setopt(HANDLE, CURLOPT_INFILESIZE, 0);
+ }
+
+ //curl_easy_setopt(HANDLE, CURLOPT_VERBOSE, 1);
+
+ curl_easy_setopt(HANDLE, CURLOPT_URL, http->url);
+ curl_easy_setopt(HANDLE, CURLOPT_HTTPHEADER, http->req_headers);
+ curl_easy_setopt(HANDLE, CURLOPT_READDATA, &state);
+ curl_easy_setopt(HANDLE, CURLOPT_SEEKDATA, &state);
+ curl_easy_setopt(HANDLE, CURLOPT_WRITEHEADER, &state);
+ curl_easy_setopt(HANDLE, CURLOPT_WRITEDATA, &state);
+
+ if(curl_easy_perform(HANDLE) != 0)
+ {
+ JS_ReportError(cx, "Failed to execute HTTP request: %s", ERRBUF);
+ goto error;
+ }
+
+ if(!state.resp_headers)
+ {
+ JS_ReportError(cx, "Failed to recieve HTTP headers.");
+ goto error;
+ }
+
+ tmp = OBJECT_TO_JSVAL(state.resp_headers);
+ if(!JS_DefineProperty(
+ cx,
+ obj,
+ "_headers",
+ tmp,
+ NULL,
+ NULL,
+ JSPROP_READONLY
+ ))
+ {
+ JS_ReportError(cx, "INTERNAL: Failed to set response headers.");
+ goto error;
+ }
+
+ if(state.recvbuf) // Is good enough?
+ {
+ state.recvbuf[state.read] = '\0';
+ jsbody = dec_string(cx, state.recvbuf, state.read+1);
+ if(!jsbody)
+ {
+ // This is so dirty its not even almost funny. I'm ignoring
+ // all sorts of content-types and character sets and just falling
+ // back to doing a chop job when something doesn't decode as UTF-8
+ // which is pretty sad. But, if you hate me for it, then feel free
+ // to write a patch that does the proper content-type parsing and
+ // actually respects charsets as returned in headers.
+ jsbody = JS_NewString(cx, state.recvbuf, state.read);
+ if(!jsbody) {
+ JS_ReportError(cx, "INTERNAL: Failed to decode body.");
+ goto error;
+ }
+ }
+ tmp = STRING_TO_JSVAL(jsbody);
+ }
+ else
+ {
+ tmp = JS_GetEmptyStringValue(cx);
+ }
+
+ if(!JS_DefineProperty(
+ cx,
+ obj,
+ "responseText",
+ tmp,
+ NULL,
+ NULL,
+ JSPROP_READONLY
+ ))
+ {
+ JS_ReportError(cx, "INTERNAL: Failed to set responseText.");
+ goto error;
+ }
+
+ ret = JS_TRUE;
+ goto success;
+
+error:
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+
+success:
+ return ret;
+}
+
+static size_t
+send_body(void *ptr, size_t size, size_t nmem, void *data)
+{
+ CurlState* state = (CurlState*) data;
+ size_t length = size * nmem;
+ size_t towrite = state->sendlen - state->sent;
+ if(towrite == 0)
+ {
+ return 0;
+ }
+
+ if(length < towrite) towrite = length;
+
+ //fprintf(stderr, "%lu %lu %lu %lu\n", state->bodyused, state->bodyread, length, towrite);
+
+ memcpy(ptr, state->sendbuf + state->sent, towrite);
+ state->sent += towrite;
+
+ return towrite;
+}
+
+static int
+seek_body(void* ptr, curl_off_t offset, int origin)
+{
+ CurlState* state = (CurlState*) ptr;
+ if(origin != SEEK_SET) return -1;
+
+ state->sent = (size_t) offset;
+ return (int) state->sent;
+}
+
+static size_t
+recv_header(void *ptr, size_t size, size_t nmem, void *data)
+{
+ CurlState* state = (CurlState*) data;
+ char code[4];
+ char* header = (char*) ptr;
+ size_t length = size * nmem;
+ size_t index = 0;
+ JSString* hdr = NULL;
+ jsuint hdrlen;
+ jsval hdrval;
+
+ if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0)
+ {
+ if(length < 12)
+ {
+ return CURLE_WRITE_ERROR;
+ }
+
+ memcpy(code, header+9, 3*sizeof(char));
+ code[3] = '\0';
+ state->http->last_status = atoi(code);
+
+ state->resp_headers = JS_NewArrayObject(state->cx, 0, NULL);
+ if(!state->resp_headers)
+ {
+ return CURLE_WRITE_ERROR;
+ }
+
+ return length;
+ }
+
+ // We get a notice at the \r\n\r\n after headers.
+ if(length <= 2)
+ {
+ return length;
+ }
+
+ // Append the new header to our array.
+ hdr = dec_string(state->cx, header, length);
+ if(!hdr)
+ {
+ return CURLE_WRITE_ERROR;
+ }
+
+ if(!JS_GetArrayLength(state->cx, state->resp_headers, &hdrlen))
+ {
+ return CURLE_WRITE_ERROR;
+ }
+
+ hdrval = STRING_TO_JSVAL(hdr);
+ if(!JS_SetElement(state->cx, state->resp_headers, hdrlen, &hdrval))
+ {
+ return CURLE_WRITE_ERROR;
+ }
+
+ return length;
+}
+
+static size_t
+recv_body(void *ptr, size_t size, size_t nmem, void *data)
+{
+ CurlState* state = (CurlState*) data;
+ size_t length = size * nmem;
+ char* tmp = NULL;
+
+ if(!state->recvbuf)
+ {
+ state->recvlen = 4096;
+ state->read = 0;
+ state->recvbuf = JS_malloc(state->cx, state->recvlen);
+ }
+
+ if(!state->recvbuf)
+ {
+ return CURLE_WRITE_ERROR;
+ }
+
+ // +1 so we can add '\0' back up in the go function.
+ while(length+1 > state->recvlen - state->read) state->recvlen *= 2;
+ tmp = JS_realloc(state->cx, state->recvbuf, state->recvlen);
+ if(!tmp) return CURLE_WRITE_ERROR;
+ state->recvbuf = tmp;
+
+ memcpy(state->recvbuf + state->read, ptr, length);
+ state->read += length;
+ return length;
+}
+
diff --git a/src/couchdb/priv/couch_js/http.h b/src/couchdb/priv/couch_js/http.h
new file mode 100644
index 00000000..b5f8c70f
--- /dev/null
+++ b/src/couchdb/priv/couch_js/http.h
@@ -0,0 +1,18 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#ifndef COUCH_JS_HTTP_H
+#define COUCH_JS_HTTP_H
+
+JSObject* install_http(JSContext* cx, JSObject* global);
+
+#endif \ No newline at end of file
diff --git a/src/couchdb/priv/couch_js/main.c b/src/couchdb/priv/couch_js/main.c
new file mode 100644
index 00000000..ee0e42c9
--- /dev/null
+++ b/src/couchdb/priv/couch_js/main.c
@@ -0,0 +1,324 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <jsapi.h>
+#include "config.h"
+
+#include "utf8.h"
+#include "http.h"
+
+int gExitCode = 0;
+
+#ifdef JS_THREADSAFE
+#define SETUP_REQUEST(cx) \
+ JS_SetContextThread(cx); \
+ JS_BeginRequest(cx);
+#define FINISH_REQUEST(cx) \
+ JS_EndRequest(cx); \
+ JS_ClearContextThread(cx);
+#else
+#define SETUP_REQUEST(cx)
+#define FINISH_REQUEST(cx)
+#endif
+
+static JSBool
+evalcx(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+ JSString *str;
+ JSObject *sandbox;
+ JSContext *subcx;
+ const jschar *src;
+ size_t srclen;
+ JSBool ret = JS_FALSE;
+ jsval v;
+
+ sandbox = NULL;
+ if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox))
+ {
+ return JS_FALSE;
+ }
+
+ subcx = JS_NewContext(JS_GetRuntime(cx), 8L * 1024L);
+ if(!subcx)
+ {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+
+ SETUP_REQUEST(subcx);
+
+ src = JS_GetStringChars(str);
+ srclen = JS_GetStringLength(str);
+
+ if(!sandbox)
+ {
+ sandbox = JS_NewObject(subcx, NULL, NULL, NULL);
+ if(!sandbox || !JS_InitStandardClasses(subcx, sandbox)) goto done;
+ }
+
+ if(srclen == 0)
+ {
+ *rval = OBJECT_TO_JSVAL(sandbox);
+ }
+ else
+ {
+ JS_EvaluateUCScript(subcx, sandbox, src, srclen, NULL, 0, rval);
+ }
+
+ ret = JS_TRUE;
+
+done:
+ FINISH_REQUEST(subcx);
+ JS_DestroyContext(subcx);
+ return ret;
+}
+
+static JSBool
+gc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+ JS_GC(cx);
+ return JS_TRUE;
+}
+
+static JSBool
+print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+ uintN i;
+ char *bytes;
+
+ for(i = 0; i < argc; i++)
+ {
+ bytes = enc_string(cx, argv[i], NULL);
+ if(!bytes) return JS_FALSE;
+
+ fprintf(stdout, "%s%s", i ? " " : "", bytes);
+ JS_free(cx, bytes);
+ }
+
+ fputc('\n', stdout);
+ fflush(stdout);
+ return JS_TRUE;
+}
+
+static JSBool
+quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+ JS_ConvertArguments(cx, argc, argv, "/ i", &gExitCode);
+ return JS_FALSE;
+}
+
+static char*
+readfp(JSContext* cx, FILE* fp, size_t* buflen)
+{
+ char* bytes = NULL;
+ char* tmp = NULL;
+ size_t used = 0;
+ size_t byteslen = 256;
+ size_t readlen = 0;
+
+ bytes = JS_malloc(cx, byteslen);
+ if(bytes == NULL) return NULL;
+
+ while((readlen = js_fgets(bytes+used, byteslen-used, stdin)) > 0)
+ {
+ used += readlen;
+
+ if(bytes[used-1] == '\n')
+ {
+ bytes[used-1] = '\0';
+ break;
+ }
+
+ // Double our buffer and read more.
+ byteslen *= 2;
+ tmp = JS_realloc(cx, bytes, byteslen);
+ if(!tmp)
+ {
+ JS_free(cx, bytes);
+ return NULL;
+ }
+ bytes = tmp;
+ }
+
+ *buflen = used;
+ return bytes;
+}
+
+static JSBool
+readline(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ jschar *chars;
+ JSString *str;
+ char* bytes;
+ char* tmp;
+ size_t byteslen;
+
+ /* GC Occasionally */
+ JS_MaybeGC(cx);
+
+ bytes = readfp(cx, stdin, &byteslen);
+ if(!bytes) return JS_FALSE;
+
+ /* Treat the empty string specially */
+ if(byteslen == 0)
+ {
+ *rval = JS_GetEmptyStringValue(cx);
+ JS_free(cx, bytes);
+ return JS_TRUE;
+ }
+
+ /* Shrink the buffer to the real size */
+ tmp = JS_realloc(cx, bytes, byteslen);
+ if(!tmp)
+ {
+ JS_free(cx, bytes);
+ return JS_FALSE;
+ }
+ bytes = tmp;
+
+ str = dec_string(cx, bytes, byteslen);
+ JS_free(cx, bytes);
+
+ if(!str) return JS_FALSE;
+
+ *rval = STRING_TO_JSVAL(str);
+
+ return JS_TRUE;
+}
+
+static JSBool
+seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+ JSObject *target;
+ JSBool deep = JS_FALSE;
+
+ if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
+ return JS_FALSE;
+ if (!target)
+ return JS_TRUE;
+ return JS_SealObject(cx, target, deep);
+}
+
+static void
+execute_script(JSContext *cx, JSObject *obj, const char *filename) {
+ FILE *file;
+ JSScript *script;
+ jsval result;
+
+ if(!filename || strcmp(filename, "-") == 0)
+ {
+ file = stdin;
+ }
+ else
+ {
+ file = fopen(filename, "r");
+ if (!file)
+ {
+ fprintf(stderr, "could not open script file %s\n", filename);
+ gExitCode = 1;
+ return;
+ }
+ }
+
+ script = JS_CompileFileHandle(cx, obj, filename, file);
+ if(script)
+ {
+ JS_ExecuteScript(cx, obj, script, &result);
+ JS_DestroyScript(cx, script);
+ }
+}
+
+static void
+printerror(JSContext *cx, const char *mesg, JSErrorReport *report)
+{
+ if(!report || !JSREPORT_IS_WARNING(report->flags))
+ {
+ fprintf(stderr, "%s\n", mesg);
+ }
+}
+
+static JSFunctionSpec global_functions[] = {
+ {"evalcx", evalcx, 0, 0, 0},
+ {"gc", gc, 0, 0, 0},
+ {"print", print, 0, 0, 0},
+ {"quit", quit, 0, 0, 0},
+ {"readline", readline, 0, 0, 0},
+ {"seal", seal, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static JSClass global_class = {
+ "GlobalClass",
+ JSCLASS_GLOBAL_FLAGS,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+int
+main(int argc, const char * argv[])
+{
+ JSRuntime* rt = NULL;
+ JSContext* cx = NULL;
+ JSObject *global = NULL;
+ int i = 0;
+
+ rt = JS_NewRuntime(64L * 1024L * 1024L);
+ if (!rt) return 1;
+
+ cx = JS_NewContext(rt, 8L * 1024L);
+ if (!cx) return 1;
+
+ JS_SetErrorReporter(cx, printerror);
+ JS_ToggleOptions(cx, JSOPTION_XML);
+
+ SETUP_REQUEST(cx);
+
+ global = JS_NewObject(cx, &global_class, NULL, NULL);
+ if (!global) return 1;
+ if (!JS_InitStandardClasses(cx, global)) return 1;
+
+ if(!JS_DefineFunctions(cx, global, global_functions))
+ {
+ return 1;
+ }
+
+ if(!install_http(cx, global))
+ {
+ return 1;
+ }
+
+ JS_SetGlobalObject(cx, global);
+
+ if(argc != 2) {
+ fprintf(stderr, "incorrect number of arguments\n\n");
+ fprintf(stderr, "usage: %s <scriptfile>\n", argv[0]);
+ return 2;
+ }
+
+ execute_script(cx, global, argv[1]);
+
+ FINISH_REQUEST(cx);
+
+ JS_DestroyContext(cx);
+ JS_DestroyRuntime(rt);
+ JS_ShutDown();
+
+ return gExitCode;
+}
diff --git a/src/couchdb/priv/couch_js/utf8.c b/src/couchdb/priv/couch_js/utf8.c
new file mode 100644
index 00000000..d6d6dd2b
--- /dev/null
+++ b/src/couchdb/priv/couch_js/utf8.c
@@ -0,0 +1,286 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <jsapi.h>
+
+static inline int
+enc_char(uint8 *utf8Buffer, uint32 ucs4Char)
+{
+ int utf8Length = 1;
+
+ if (ucs4Char < 0x80)
+ {
+ *utf8Buffer = (uint8)ucs4Char;
+ }
+ else
+ {
+ int i;
+ uint32 a = ucs4Char >> 11;
+ utf8Length = 2;
+ while(a)
+ {
+ a >>= 5;
+ utf8Length++;
+ }
+ i = utf8Length;
+ while(--i)
+ {
+ utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80);
+ ucs4Char >>= 6;
+ }
+ *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
+ }
+
+ return utf8Length;
+}
+
+static JSBool
+enc_charbuf(const jschar* src, size_t srclen, char* dst, size_t* dstlenp)
+{
+ size_t i;
+ size_t utf8Len;
+ size_t dstlen = *dstlenp;
+ size_t origDstlen = dstlen;
+ jschar c;
+ jschar c2;
+ uint32 v;
+ uint8 utf8buf[6];
+
+ if(!dst)
+ {
+ dstlen = origDstlen = (size_t) -1;
+ }
+
+ while(srclen)
+ {
+ c = *src++;
+ srclen--;
+
+ if((c >= 0xDC00) && (c <= 0xDFFF)) goto bad_surrogate;
+
+ if(c < 0xD800 || c > 0xDBFF)
+ {
+ v = c;
+ }
+ else
+ {
+ if(srclen < 1) goto buffer_too_small;
+ c2 = *src++;
+ srclen--;
+ if ((c2 < 0xDC00) || (c2 > 0xDFFF))
+ {
+ c = c2;
+ goto bad_surrogate;
+ }
+ v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
+ }
+ if(v < 0x0080)
+ {
+ /* no encoding necessary - performance hack */
+ if(!dstlen) goto buffer_too_small;
+ if(dst) *dst++ = (char) v;
+ utf8Len = 1;
+ }
+ else
+ {
+ utf8Len = enc_char(utf8buf, v);
+ if(utf8Len > dstlen) goto buffer_too_small;
+ if(dst)
+ {
+ for (i = 0; i < utf8Len; i++)
+ {
+ *dst++ = (char) utf8buf[i];
+ }
+ }
+ }
+ dstlen -= utf8Len;
+ }
+
+ *dstlenp = (origDstlen - dstlen);
+ return JS_TRUE;
+
+bad_surrogate:
+ *dstlenp = (origDstlen - dstlen);
+ return JS_FALSE;
+
+buffer_too_small:
+ *dstlenp = (origDstlen - dstlen);
+ return JS_FALSE;
+}
+
+char*
+enc_string(JSContext* cx, jsval arg, size_t* buflen)
+{
+ JSString* str = NULL;
+ jschar* src = NULL;
+ char* bytes = NULL;
+ size_t srclen = 0;
+ size_t byteslen = 0;
+
+ str = JS_ValueToString(cx, arg);
+ if(!str) goto error;
+
+ src = JS_GetStringChars(str);
+ srclen = JS_GetStringLength(str);
+
+ if(!enc_charbuf(src, srclen, NULL, &byteslen)) goto error;
+
+ bytes = JS_malloc(cx, (byteslen) + 1);
+ bytes[byteslen] = 0;
+
+ if(!enc_charbuf(src, srclen, bytes, &byteslen)) goto error;
+
+ if(buflen) *buflen = byteslen;
+ goto success;
+
+error:
+ if(bytes != NULL) JS_free(cx, bytes);
+ bytes = NULL;
+
+success:
+ return bytes;
+}
+
+static inline uint32
+dec_char(const uint8 *utf8Buffer, int utf8Length)
+{
+ uint32 ucs4Char;
+ uint32 minucs4Char;
+
+ /* from Unicode 3.1, non-shortest form is illegal */
+ static const uint32 minucs4Table[] = {
+ 0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000
+ };
+
+ if (utf8Length == 1)
+ {
+ ucs4Char = *utf8Buffer;
+ }
+ else
+ {
+ ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
+ minucs4Char = minucs4Table[utf8Length-2];
+ while(--utf8Length)
+ {
+ ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
+ }
+ if(ucs4Char < minucs4Char || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF)
+ {
+ ucs4Char = 0xFFFD;
+ }
+ }
+
+ return ucs4Char;
+}
+
+static JSBool
+dec_charbuf(const char *src, size_t srclen, jschar *dst, size_t *dstlenp)
+{
+ uint32 v;
+ size_t offset = 0;
+ size_t j;
+ size_t n;
+ size_t dstlen = *dstlenp;
+ size_t origDstlen = dstlen;
+
+ if(!dst) dstlen = origDstlen = (size_t) -1;
+
+ while(srclen)
+ {
+ v = (uint8) *src;
+ n = 1;
+
+ if(v & 0x80)
+ {
+ while(v & (0x80 >> n))
+ {
+ n++;
+ }
+
+ if(n > srclen) goto buffer_too_small;
+ if(n == 1 || n > 6) goto bad_character;
+
+ for(j = 1; j < n; j++)
+ {
+ if((src[j] & 0xC0) != 0x80) goto bad_character;
+ }
+
+ v = dec_char((const uint8 *) src, n);
+ if(v >= 0x10000)
+ {
+ v -= 0x10000;
+
+ if(v > 0xFFFFF || dstlen < 2)
+ {
+ *dstlenp = (origDstlen - dstlen);
+ return JS_FALSE;
+ }
+
+ if(dstlen < 2) goto buffer_too_small;
+
+ if(dst)
+ {
+ *dst++ = (jschar)((v >> 10) + 0xD800);
+ v = (jschar)((v & 0x3FF) + 0xDC00);
+ }
+ dstlen--;
+ }
+ }
+
+ if(!dstlen) goto buffer_too_small;
+ if(dst) *dst++ = (jschar) v;
+
+ dstlen--;
+ offset += n;
+ src += n;
+ srclen -= n;
+ }
+
+ *dstlenp = (origDstlen - dstlen);
+ return JS_TRUE;
+
+bad_character:
+ *dstlenp = (origDstlen - dstlen);
+ return JS_FALSE;
+
+buffer_too_small:
+ *dstlenp = (origDstlen - dstlen);
+ return JS_FALSE;
+}
+
+JSString*
+dec_string(JSContext* cx, const char* bytes, size_t byteslen)
+{
+ JSString* str = NULL;
+ jschar* chars = NULL;
+ size_t charslen;
+
+ if(!dec_charbuf(bytes, byteslen, NULL, &charslen)) goto error;
+
+ chars = JS_malloc(cx, (charslen + 1) * sizeof(jschar));
+ if(!chars) return NULL;
+ chars[charslen] = 0;
+
+ if(!dec_charbuf(bytes, byteslen, chars, &charslen)) goto error;
+
+ str = JS_NewUCString(cx, chars, charslen - 1);
+ if(!str) goto error;
+
+ goto success;
+
+error:
+ if(chars != NULL) JS_free(cx, chars);
+ str = NULL;
+
+success:
+ return str;
+} \ No newline at end of file
diff --git a/src/couchdb/priv/couch_js/utf8.h b/src/couchdb/priv/couch_js/utf8.h
new file mode 100644
index 00000000..00f6b736
--- /dev/null
+++ b/src/couchdb/priv/couch_js/utf8.h
@@ -0,0 +1,19 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#ifndef COUCH_JS_UTF_8_H
+#define COUCH_JS_UTF_8_H
+
+char* enc_string(JSContext* cx, jsval arg, size_t* buflen);
+JSString* dec_string(JSContext* cx, const char* buf, size_t buflen);
+
+#endif \ No newline at end of file