From c452048cae6ff8b275e72a2f4b590ccaeebc35ba Mon Sep 17 00:00:00 2001 From: Paul Joseph Davis Date: Thu, 26 Nov 2009 19:29:35 +0000 Subject: Move all C code to src/couchdb/priv Shuffling bits around to conform to Erlang conventions. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@884671 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 4 +- license.skip | 8 +- src/couchdb/Makefile.am | 43 - src/couchdb/couch_erl_driver.c | 177 --- src/couchdb/couch_js.c | 1286 -------------------- src/couchdb/couch_util.erl | 6 +- src/couchdb/curlhelper.c | 255 ---- src/couchdb/curlhelper.h | 49 - src/couchdb/priv/Makefile.am | 28 +- src/couchdb/priv/couch_js/couch_js.c | 1286 ++++++++++++++++++++ src/couchdb/priv/couch_js/curlhelper.c | 255 ++++ src/couchdb/priv/couch_js/curlhelper.h | 49 + src/couchdb/priv/couchspawnkillable.sh | 20 - src/couchdb/priv/couchspawnkillable_win.c | 139 --- src/couchdb/priv/icu_driver/couch_icu_driver.c | 177 +++ .../priv/spawnkillable/couchspawnkillable.sh | 20 + .../priv/spawnkillable/couchspawnkillable_win.c | 139 +++ test/etap/002-erl-driver.t | 33 - test/etap/002-icu-driver.t | 33 + 19 files changed, 1992 insertions(+), 2015 deletions(-) delete mode 100644 src/couchdb/couch_erl_driver.c delete mode 100644 src/couchdb/couch_js.c delete mode 100644 src/couchdb/curlhelper.c delete mode 100644 src/couchdb/curlhelper.h create mode 100644 src/couchdb/priv/couch_js/couch_js.c create mode 100644 src/couchdb/priv/couch_js/curlhelper.c create mode 100644 src/couchdb/priv/couch_js/curlhelper.h delete mode 100755 src/couchdb/priv/couchspawnkillable.sh delete mode 100644 src/couchdb/priv/couchspawnkillable_win.c create mode 100644 src/couchdb/priv/icu_driver/couch_icu_driver.c create mode 100644 src/couchdb/priv/spawnkillable/couchspawnkillable.sh create mode 100644 src/couchdb/priv/spawnkillable/couchspawnkillable_win.c delete mode 100644 test/etap/002-erl-driver.t create mode 100644 test/etap/002-icu-driver.t diff --git a/.gitignore b/.gitignore index 6bbffabd..69e07e9a 100644 --- a/.gitignore +++ b/.gitignore @@ -49,12 +49,14 @@ etc/launchd/org.apache.couchdb.plist etc/logrotate.d/couchdb src/couchdb/.libs/* src/couchdb/couch.app -src/couchdb/couch_erl_driver.la src/couchdb/couchjs src/couchdb/edoc-info src/couchdb/erlang.png src/couchdb/stylesheet.css src/couchdb/priv/.deps/ +src/couchdb/priv/.libs/ +src/couchdb/priv/couch_icu_driver.la +src/couchdb/priv/couchjs src/couchdb/priv/couchspawnkillable test/local.ini test/etap/test_util.erl diff --git a/license.skip b/license.skip index e88d42d2..f33902ee 100644 --- a/license.skip +++ b/license.skip @@ -53,17 +53,19 @@ ^share/www/script/test/lorem* ^src/Makefile ^src/Makefile.in -^src/couchdb/*.beam +^src/couchdb/.*beam ^src/couchdb/couch.app.tpl.in ^src/couchdb/.deps/* -^src/couchdb/.libs/* +^src/couchdb/couch.app* ^src/couchdb/Makefile ^src/couchdb/Makefile.in -^src/couchdb/*.o ^src/couchdb/priv/.deps/* +^src/couchdb/priv/.*o ^src/couchdb/priv/Makefile ^src/couchdb/priv/Makefile.in +^src/couchdb/priv/couchjs ^src/couchdb/priv/couchspawnkillable +^src/couchdb/priv/couch_icu_driver.la ^src/couchdb/priv/stat_descriptions.cfg ^src/erlang-oauth/* ^src/etap/* diff --git a/src/couchdb/Makefile.am b/src/couchdb/Makefile.am index 5842521b..9e909964 100644 --- a/src/couchdb/Makefile.am +++ b/src/couchdb/Makefile.am @@ -12,38 +12,12 @@ SUBDIRS = priv -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 - # devdocdir = $(localdocdir)/developer/couchdb couchlibdir = $(localerlanglibdir)/couch-$(version) -couchprivdir = $(couchlibdir)/priv -couchprivlibdir = $(couchlibdir)/priv/lib couchincludedir = $(couchlibdir)/include couchebindir = $(couchlibdir)/ebin -couchprivlib_LTLIBRARIES = couch_erl_driver.la -couch_erl_driver_la_SOURCES = couch_erl_driver.c -couch_erl_driver_la_LDFLAGS = -module -avoid-version $(ICU_LOCAL_FLAGS) -couch_erl_driver_la_CFLAGS = $(ICU_LOCAL_FLAGS) -couch_erl_driver_la_LIBADD = $(ICU_LOCAL_LIBS) - -locallibbin_PROGRAMS = couchjs -couchjs_SOURCES = couch_js.c curlhelper.c curlhelper.h -couchjs_LDFLAGS = $(CURL_LDFLAGS) -couchjs_CFLAGS = $(CURL_CFLAGS) -couchjs_LDADD = $(CURL_LDFLAGS) @JSLIB@ - -if WINDOWS -couch_erl_driver_la_LDFLAGS += -no-undefined -endif - couchinclude_DATA = couch_db.hrl - couchebin_DATA = $(compiled_files) # dist_devdoc_DATA = $(doc_base) $(doc_modules) @@ -219,20 +193,3 @@ endif %.beam: %.erl couch_db.hrl $(ERLC) $(ERLC_FLAGS) ${TEST} $<; -install-data-hook: - if test -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver"; then \ - rm -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver.so"; \ - cd "$(DESTDIR)$(couchprivlibdir)" && \ - $(LN_S) couch_erl_driver couch_erl_driver.so; \ - fi -if WINDOWS - $(INSTALL) $(ICU_LOCAL_BIN)/icuuc42.dll $(bindir) - $(INSTALL) $(ICU_LOCAL_BIN)/icudt42.dll $(bindir) - $(INSTALL) $(ICU_LOCAL_BIN)/icuin42.dll $(bindir) - $(INSTALL) $(JS_LIB_BINARY) $(bindir) -endif - -uninstall-local: - if test -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver"; then \ - rm -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver.so"; \ - fi diff --git a/src/couchdb/couch_erl_driver.c b/src/couchdb/couch_erl_driver.c deleted file mode 100644 index 07f33867..00000000 --- a/src/couchdb/couch_erl_driver.c +++ /dev/null @@ -1,177 +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. - -*/ - -// This file is the C port driver for Erlang. It provides a low overhead -// means of calling into C code, however coding errors in this module can -// crash the entire Erlang server. - -#ifdef DARWIN -#define U_HIDE_DRAFT_API 1 -#define U_DISABLE_RENAMING 1 -#endif - -#include "erl_driver.h" -#include "unicode/ucol.h" -#include "unicode/ucasemap.h" -#ifndef WIN32 -#include // for memcpy -#endif - -typedef struct { - ErlDrvPort port; - UCollator* collNoCase; - UCollator* coll; -} couch_drv_data; - -static void couch_drv_stop(ErlDrvData data) -{ - couch_drv_data* pData = (couch_drv_data*)data; - if (pData->coll) { - ucol_close(pData->coll); - } - if (pData->collNoCase) { - ucol_close(pData->collNoCase); - } - driver_free((char*)pData); -} - -static ErlDrvData couch_drv_start(ErlDrvPort port, char *buff) -{ - UErrorCode status = U_ZERO_ERROR; - couch_drv_data* pData = (couch_drv_data*)driver_alloc(sizeof(couch_drv_data)); - - if (pData == NULL) - return ERL_DRV_ERROR_GENERAL; - - pData->port = port; - - pData->coll = ucol_open("", &status); - if (U_FAILURE(status)) { - couch_drv_stop((ErlDrvData)pData); - return ERL_DRV_ERROR_GENERAL; - } - - pData->collNoCase = ucol_open("", &status); - if (U_FAILURE(status)) { - couch_drv_stop((ErlDrvData)pData); - return ERL_DRV_ERROR_GENERAL; - } - - ucol_setAttribute(pData->collNoCase, UCOL_STRENGTH, UCOL_PRIMARY, &status); - if (U_FAILURE(status)) { - couch_drv_stop((ErlDrvData)pData); - return ERL_DRV_ERROR_GENERAL; - } - - return (ErlDrvData)pData; -} - -static int return_control_result(void* pLocalResult, int localLen, char **ppRetBuf, int returnLen) -{ - if (*ppRetBuf == NULL || localLen > returnLen) { - *ppRetBuf = (char*)driver_alloc_binary(localLen); - if(*ppRetBuf == NULL) { - return -1; - } - } - memcpy(*ppRetBuf, pLocalResult, localLen); - return localLen; -} - -static int couch_drv_control(ErlDrvData drv_data, unsigned int command, char *pBuf, - int bufLen, char **rbuf, int rlen) -{ - - couch_drv_data* pData = (couch_drv_data*)drv_data; - switch(command) { - case 0: // COLLATE - case 1: // COLLATE_NO_CASE: - { - UErrorCode status = U_ZERO_ERROR; - int collResult; - char response; - UCharIterator iterA; - UCharIterator iterB; - int32_t length; - - // 2 strings are in the buffer, consecutively - // The strings begin first with a 32 bit integer byte length, then the actual - // string bytes follow. - - // first 32bits are the length - memcpy(&length, pBuf, sizeof(length)); - pBuf += sizeof(length); - - // point the iterator at it. - uiter_setUTF8(&iterA, pBuf, length); - - pBuf += length; // now on to string b - - // first 32bits are the length - memcpy(&length, pBuf, sizeof(length)); - pBuf += sizeof(length); - - // point the iterator at it. - uiter_setUTF8(&iterB, pBuf, length); - - if (command == 0) // COLLATE - collResult = ucol_strcollIter(pData->coll, &iterA, &iterB, &status); - else // COLLATE_NO_CASE - collResult = ucol_strcollIter(pData->collNoCase, &iterA, &iterB, &status); - - if (collResult < 0) - response = 0; //lt - else if (collResult > 0) - response = 2; //gt - else - response = 1; //eq - - return return_control_result(&response, sizeof(response), rbuf, rlen); - } - - default: - return -1; - } -} - -ErlDrvEntry couch_driver_entry = { - NULL, /* F_PTR init, N/A */ - couch_drv_start, /* L_PTR start, called when port is opened */ - couch_drv_stop, /* F_PTR stop, called when port is closed */ - NULL, /* F_PTR output, called when erlang has sent */ - NULL, /* F_PTR ready_input, called when input descriptor ready */ - NULL, /* F_PTR ready_output, called when output descriptor ready */ - "couch_erl_driver", /* char *driver_name, the argument to open_port */ - NULL, /* F_PTR finish, called when unloaded */ - NULL, /* Not used */ - couch_drv_control, /* F_PTR control, port_command callback */ - NULL, /* F_PTR timeout, reserved */ - NULL, /* F_PTR outputv, reserved */ - NULL, /* F_PTR ready_async */ - NULL, /* F_PTR flush */ - NULL, /* F_PTR call */ - NULL, /* F_PTR event */ - ERL_DRV_EXTENDED_MARKER, - ERL_DRV_EXTENDED_MAJOR_VERSION, - ERL_DRV_EXTENDED_MINOR_VERSION, - ERL_DRV_FLAG_USE_PORT_LOCKING, - NULL, /* Reserved -- Used by emulator internally */ - NULL, /* F_PTR process_exit */ -}; - -DRIVER_INIT(couch_erl_driver) /* must match name in driver_entry */ -{ - return &couch_driver_entry; -} diff --git a/src/couchdb/couch_js.c b/src/couchdb/couch_js.c deleted file mode 100644 index 0acc5b55..00000000 --- a/src/couchdb/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 -#include -#include -#include "curlhelper.h" -#include -#include -#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 \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/couch_util.erl b/src/couchdb/couch_util.erl index da62375c..8e2c66df 100644 --- a/src/couchdb/couch_util.erl +++ b/src/couchdb/couch_util.erl @@ -39,11 +39,11 @@ priv_dir() -> end. start_driver(LibDir) -> - case erl_ddll:load_driver(LibDir, "couch_erl_driver") of + case erl_ddll:load_driver(LibDir, "couch_icu_driver") of ok -> ok; {error, already_loaded} -> - ok = erl_ddll:reload_driver(LibDir, "couch_erl_driver"); + ok = erl_ddll:reload_driver(LibDir, "couch_icu_driver"); {error, Error} -> exit(erl_ddll:format_error(Error)) end. @@ -191,7 +191,7 @@ implode([H|T], Sep, Acc) -> drv_port() -> case get(couch_drv_port) of undefined -> - Port = open_port({spawn, "couch_erl_driver"}, []), + Port = open_port({spawn, "couch_icu_driver"}, []), put(couch_drv_port, Port), Port; Port -> diff --git a/src/couchdb/curlhelper.c b/src/couchdb/curlhelper.c deleted file mode 100644 index 738ac64a..00000000 --- a/src/couchdb/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 -#include -#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/curlhelper.h b/src/couchdb/curlhelper.h deleted file mode 100644 index 098bdf02..00000000 --- a/src/couchdb/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/Makefile.am b/src/couchdb/priv/Makefile.am index fecaa64b..d4c759c1 100644 --- a/src/couchdb/priv/Makefile.am +++ b/src/couchdb/priv/Makefile.am @@ -12,6 +12,7 @@ couchlibdir = $(localerlanglibdir)/couch-$(version) couchprivdir = $(couchlibdir)/priv +couchprivlibdir = $(couchlibdir)/priv/lib EXTRA_DIST = \ couchspawnkillable.sh \ @@ -26,21 +27,36 @@ couchpriv_PROGRAMS = couchspawnkillable cp $< $@ if WINDOWS -couchspawnkillable_SOURCES = couchspawnkillable_win.c +couchspawnkillable_SOURCES = spawnkillable/couchspawnkillable_win.c endif if !WINDOWS -couchspawnkillable: couchspawnkillable.sh +couchspawnkillable: spawnkillable/couchspawnkillable.sh cp $< $@ endif -if WINDOWS -install-data-hook: # libtool and automake have defeated markh. For each of our executables # we end up with 2 copies - one directly in the 'target' folder (eg, 'priv') # and another - the correct one - in .libs. The former doesn't work but is # what gets installed for 'couchspawnkillable' - but the correct one for # couchjs.exe *does* get copied. *shrug* So just clobber it with the -# correct one here... See bug COUCHDB-439 - $(INSTALL) .libs/couchspawnkillable.exe "$(DESTDIR)$(couchprivdir)/couchspawnkillable.exe" +# correct one as the last step. See bug COUCHDB-439 +install-data-hook: + if test -f "$(DESTDIR)$(couchprivlibdir)/couch_icu_driver"; then \ + rm -f "$(DESTDIR)$(couchprivlibdir)/couch_icu_driver.so"; \ + cd "$(DESTDIR)$(couchprivlibdir)" && \ + $(LN_S) couch_icu_driver couch_icu_driver.so; \ + fi +if WINDOWS + $(INSTALL) $(ICU_LOCAL_BIN)/icuuc42.dll $(bindir) + $(INSTALL) $(ICU_LOCAL_BIN)/icudt42.dll $(bindir) + $(INSTALL) $(ICU_LOCAL_BIN)/icuin42.dll $(bindir) + $(INSTALL) $(JS_LIB_BINARY) $(bindir) + $(INSTALL) .libs/couchspawnkillable.exe \ + "$(DESTDIR)$(couchprivdir)/couchspawnkillable.exe" endif + +uninstall-local: + if test -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver"; then \ + rm -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver.so"; \ + fi diff --git a/src/couchdb/priv/couch_js/couch_js.c b/src/couchdb/priv/couch_js/couch_js.c new file mode 100644 index 00000000..0acc5b55 --- /dev/null +++ b/src/couchdb/priv/couch_js/couch_js.c @@ -0,0 +1,1286 @@ +/* + +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 +#include +#include +#include "curlhelper.h" +#include +#include +#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 \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 new file mode 100644 index 00000000..738ac64a --- /dev/null +++ b/src/couchdb/priv/couch_js/curlhelper.c @@ -0,0 +1,255 @@ +/* + +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 +#include +#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 new file mode 100644 index 00000000..098bdf02 --- /dev/null +++ b/src/couchdb/priv/couch_js/curlhelper.h @@ -0,0 +1,49 @@ +/* + +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/couchspawnkillable.sh b/src/couchdb/priv/couchspawnkillable.sh deleted file mode 100755 index f8d042e3..00000000 --- a/src/couchdb/priv/couchspawnkillable.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /bin/sh -e - -# 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. - -# The purpose of this script is to echo an OS specific command before launching -# the actual process. This provides a way for Erlang to hard-kill its external -# processes. - -echo "kill -9 $$" -exec $* diff --git a/src/couchdb/priv/couchspawnkillable_win.c b/src/couchdb/priv/couchspawnkillable_win.c deleted file mode 100644 index f812711e..00000000 --- a/src/couchdb/priv/couchspawnkillable_win.c +++ /dev/null @@ -1,139 +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. - -// Do what 2 lines of shell script in couchspawnkillable does... -// * Create a new suspended process with the same (duplicated) standard -// handles as us. -// * Write a line to stdout, consisting of the path to ourselves, plus -// '--kill {pid}' where {pid} is the PID of the newly created process. -// * Un-suspend the new process. -// * Terminate - -// Later, couch will call us with --kill and the PID, so we dutifully -// terminate the specified PID. - -#include -#include "windows.h" - -char *get_child_cmdline(int argc, char **argv) -{ - // make a new command-line, but skipping me. - // XXX - todo - spaces etc in args??? - int i; - char *p, *cmdline; - int nchars = 0; - int nthis = 1; - for (i=1;i // for memcpy +#endif + +typedef struct { + ErlDrvPort port; + UCollator* collNoCase; + UCollator* coll; +} couch_drv_data; + +static void couch_drv_stop(ErlDrvData data) +{ + couch_drv_data* pData = (couch_drv_data*)data; + if (pData->coll) { + ucol_close(pData->coll); + } + if (pData->collNoCase) { + ucol_close(pData->collNoCase); + } + driver_free((char*)pData); +} + +static ErlDrvData couch_drv_start(ErlDrvPort port, char *buff) +{ + UErrorCode status = U_ZERO_ERROR; + couch_drv_data* pData = (couch_drv_data*)driver_alloc(sizeof(couch_drv_data)); + + if (pData == NULL) + return ERL_DRV_ERROR_GENERAL; + + pData->port = port; + + pData->coll = ucol_open("", &status); + if (U_FAILURE(status)) { + couch_drv_stop((ErlDrvData)pData); + return ERL_DRV_ERROR_GENERAL; + } + + pData->collNoCase = ucol_open("", &status); + if (U_FAILURE(status)) { + couch_drv_stop((ErlDrvData)pData); + return ERL_DRV_ERROR_GENERAL; + } + + ucol_setAttribute(pData->collNoCase, UCOL_STRENGTH, UCOL_PRIMARY, &status); + if (U_FAILURE(status)) { + couch_drv_stop((ErlDrvData)pData); + return ERL_DRV_ERROR_GENERAL; + } + + return (ErlDrvData)pData; +} + +static int return_control_result(void* pLocalResult, int localLen, char **ppRetBuf, int returnLen) +{ + if (*ppRetBuf == NULL || localLen > returnLen) { + *ppRetBuf = (char*)driver_alloc_binary(localLen); + if(*ppRetBuf == NULL) { + return -1; + } + } + memcpy(*ppRetBuf, pLocalResult, localLen); + return localLen; +} + +static int couch_drv_control(ErlDrvData drv_data, unsigned int command, char *pBuf, + int bufLen, char **rbuf, int rlen) +{ + + couch_drv_data* pData = (couch_drv_data*)drv_data; + switch(command) { + case 0: // COLLATE + case 1: // COLLATE_NO_CASE: + { + UErrorCode status = U_ZERO_ERROR; + int collResult; + char response; + UCharIterator iterA; + UCharIterator iterB; + int32_t length; + + // 2 strings are in the buffer, consecutively + // The strings begin first with a 32 bit integer byte length, then the actual + // string bytes follow. + + // first 32bits are the length + memcpy(&length, pBuf, sizeof(length)); + pBuf += sizeof(length); + + // point the iterator at it. + uiter_setUTF8(&iterA, pBuf, length); + + pBuf += length; // now on to string b + + // first 32bits are the length + memcpy(&length, pBuf, sizeof(length)); + pBuf += sizeof(length); + + // point the iterator at it. + uiter_setUTF8(&iterB, pBuf, length); + + if (command == 0) // COLLATE + collResult = ucol_strcollIter(pData->coll, &iterA, &iterB, &status); + else // COLLATE_NO_CASE + collResult = ucol_strcollIter(pData->collNoCase, &iterA, &iterB, &status); + + if (collResult < 0) + response = 0; //lt + else if (collResult > 0) + response = 2; //gt + else + response = 1; //eq + + return return_control_result(&response, sizeof(response), rbuf, rlen); + } + + default: + return -1; + } +} + +ErlDrvEntry couch_driver_entry = { + NULL, /* F_PTR init, N/A */ + couch_drv_start, /* L_PTR start, called when port is opened */ + couch_drv_stop, /* F_PTR stop, called when port is closed */ + NULL, /* F_PTR output, called when erlang has sent */ + NULL, /* F_PTR ready_input, called when input descriptor ready */ + NULL, /* F_PTR ready_output, called when output descriptor ready */ + "couch_icu_driver", /* char *driver_name, the argument to open_port */ + NULL, /* F_PTR finish, called when unloaded */ + NULL, /* Not used */ + couch_drv_control, /* F_PTR control, port_command callback */ + NULL, /* F_PTR timeout, reserved */ + NULL, /* F_PTR outputv, reserved */ + NULL, /* F_PTR ready_async */ + NULL, /* F_PTR flush */ + NULL, /* F_PTR call */ + NULL, /* F_PTR event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + ERL_DRV_FLAG_USE_PORT_LOCKING, + NULL, /* Reserved -- Used by emulator internally */ + NULL, /* F_PTR process_exit */ +}; + +DRIVER_INIT(couch_icu_driver) /* must match name in driver_entry */ +{ + return &couch_driver_entry; +} diff --git a/src/couchdb/priv/spawnkillable/couchspawnkillable.sh b/src/couchdb/priv/spawnkillable/couchspawnkillable.sh new file mode 100644 index 00000000..f8d042e3 --- /dev/null +++ b/src/couchdb/priv/spawnkillable/couchspawnkillable.sh @@ -0,0 +1,20 @@ +#! /bin/sh -e + +# 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. + +# The purpose of this script is to echo an OS specific command before launching +# the actual process. This provides a way for Erlang to hard-kill its external +# processes. + +echo "kill -9 $$" +exec $* diff --git a/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c b/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c new file mode 100644 index 00000000..f812711e --- /dev/null +++ b/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c @@ -0,0 +1,139 @@ +// 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. + +// Do what 2 lines of shell script in couchspawnkillable does... +// * Create a new suspended process with the same (duplicated) standard +// handles as us. +// * Write a line to stdout, consisting of the path to ourselves, plus +// '--kill {pid}' where {pid} is the PID of the newly created process. +// * Un-suspend the new process. +// * Terminate + +// Later, couch will call us with --kill and the PID, so we dutifully +// terminate the specified PID. + +#include +#include "windows.h" + +char *get_child_cmdline(int argc, char **argv) +{ + // make a new command-line, but skipping me. + // XXX - todo - spaces etc in args??? + int i; + char *p, *cmdline; + int nchars = 0; + int nthis = 1; + for (i=1;i - test_util:init_code_path(), - etap:plan(3), - etap:is( - couch_util:start_driver("src/couchdb/.libs"), - ok, - "Started couch_erl_driver." - ), - etap:is( - couch_util:collate(<<"foo">>, <<"bar">>), - 1, - "Can collate stuff" - ), - etap:is( - couch_util:collate(<<"A">>, <<"aa">>), - -1, - "Collate's non-ascii style." - ), - etap:end_tests(). diff --git a/test/etap/002-icu-driver.t b/test/etap/002-icu-driver.t new file mode 100644 index 00000000..d70f3303 --- /dev/null +++ b/test/etap/002-icu-driver.t @@ -0,0 +1,33 @@ +#!/usr/bin/env escript +% 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. + + +main(_) -> + test_util:init_code_path(), + etap:plan(3), + etap:is( + couch_util:start_driver("src/couchdb/priv/.libs"), + ok, + "Started couch_icu_driver." + ), + etap:is( + couch_util:collate(<<"foo">>, <<"bar">>), + 1, + "Can collate stuff" + ), + etap:is( + couch_util:collate(<<"A">>, <<"aa">>), + -1, + "Collate's non-ascii style." + ), + etap:end_tests(). -- cgit v1.2.3