diff options
author | Paul Joseph Davis <davisp@apache.org> | 2009-11-26 19:29:35 +0000 |
---|---|---|
committer | Paul Joseph Davis <davisp@apache.org> | 2009-11-26 19:29:35 +0000 |
commit | c452048cae6ff8b275e72a2f4b590ccaeebc35ba (patch) | |
tree | 1f3948d9b61173c0b4a81557708aad0f6e4de763 /src/couchdb/priv/couch_js | |
parent | 683834888cfad2e6da1564161eda3447620374f5 (diff) |
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
Diffstat (limited to 'src/couchdb/priv/couch_js')
-rw-r--r-- | src/couchdb/priv/couch_js/couch_js.c | 1286 | ||||
-rw-r--r-- | src/couchdb/priv/couch_js/curlhelper.c | 255 | ||||
-rw-r--r-- | src/couchdb/priv/couch_js/curlhelper.h | 49 |
3 files changed, 1590 insertions, 0 deletions
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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "curlhelper.h" +#include <jsapi.h> +#include <curl/curl.h> +#include "config.h" + +#ifndef CURLOPT_COPYPOSTFIELDS + #define CURLOPT_COPYPOSTFIELDS 10165 +#endif + +int gExitCode = 0; +int gStackChunkSize = 8L * 1024L; + +int +EncodeChar(uint8 *utf8Buffer, uint32 ucs4Char) { + int utf8Length = 1; + + if (ucs4Char < 0x80) { + *utf8Buffer = (uint8)ucs4Char; + } else { + int i; + uint32 a = ucs4Char >> 11; + utf8Length = 2; + while (a) { + a >>= 5; + utf8Length++; + } + i = utf8Length; + while (--i) { + utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80); + ucs4Char >>= 6; + } + *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char); + } + return utf8Length; +} + +JSBool +EncodeString(const jschar *src, size_t srclen, char *dst, size_t *dstlenp) { + size_t i, utf8Len, dstlen = *dstlenp, origDstlen = dstlen; + jschar c, c2; + uint32 v; + uint8 utf8buf[6]; + + if (!dst) + dstlen = origDstlen = (size_t) -1; + + while (srclen) { + c = *src++; + srclen--; + if ((c >= 0xDC00) && (c <= 0xDFFF)) + goto badSurrogate; + if (c < 0xD800 || c > 0xDBFF) { + v = c; + } else { + if (srclen < 1) + goto bufferTooSmall; + c2 = *src++; + srclen--; + if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { + c = c2; + goto badSurrogate; + } + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; + } + if (v < 0x0080) { + /* no encoding necessary - performance hack */ + if (!dstlen) + goto bufferTooSmall; + if (dst) + *dst++ = (char) v; + utf8Len = 1; + } else { + utf8Len = EncodeChar(utf8buf, v); + if (utf8Len > dstlen) + goto bufferTooSmall; + if (dst) { + for (i = 0; i < utf8Len; i++) + *dst++ = (char) utf8buf[i]; + } + } + dstlen -= utf8Len; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badSurrogate: + *dstlenp = (origDstlen - dstlen); + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + return JS_FALSE; +} + +static uint32 +DecodeChar(const uint8 *utf8Buffer, int utf8Length) { + uint32 ucs4Char; + uint32 minucs4Char; + /* from Unicode 3.1, non-shortest form is illegal */ + static const uint32 minucs4Table[] = { + 0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000 + }; + + if (utf8Length == 1) { + ucs4Char = *utf8Buffer; + } else { + ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1); + minucs4Char = minucs4Table[utf8Length-2]; + while (--utf8Length) { + ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); + } + if (ucs4Char < minucs4Char || + ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { + ucs4Char = 0xFFFD; + } + } + return ucs4Char; +} + +JSBool +DecodeString(const char *src, size_t srclen, jschar *dst, size_t *dstlenp) { + uint32 v; + size_t offset = 0, j, n, dstlen = *dstlenp, origDstlen = dstlen; + + if (!dst) + dstlen = origDstlen = (size_t) -1; + + while (srclen) { + v = (uint8) *src; + n = 1; + if (v & 0x80) { + while (v & (0x80 >> n)) + n++; + if (n > srclen) + goto bufferTooSmall; + if (n == 1 || n > 6) + goto badCharacter; + for (j = 1; j < n; j++) { + if ((src[j] & 0xC0) != 0x80) + goto badCharacter; + } + v = DecodeChar((const uint8 *) src, n); + if (v >= 0x10000) { + v -= 0x10000; + if (v > 0xFFFFF || dstlen < 2) { + *dstlenp = (origDstlen - dstlen); + return JS_FALSE; + } + if (dstlen < 2) + goto bufferTooSmall; + if (dst) { + *dst++ = (jschar)((v >> 10) + 0xD800); + v = (jschar)((v & 0x3FF) + 0xDC00); + } + dstlen--; + } + } + if (!dstlen) + goto bufferTooSmall; + if (dst) + *dst++ = (jschar) v; + dstlen--; + offset += n; + src += n; + srclen -= n; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badCharacter: + *dstlenp = (origDstlen - dstlen); + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + return JS_FALSE; +} + +static JSBool +EvalInContext(JSContext *context, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) { + JSString *str; + JSObject *sandbox; + JSContext *sub_context; + const jschar *src; + size_t srclen; + JSBool ok; + jsval v; + + sandbox = NULL; + if (!JS_ConvertArguments(context, argc, argv, "S / o", &str, &sandbox)) + return JS_FALSE; + + sub_context = JS_NewContext(JS_GetRuntime(context), gStackChunkSize); + if (!sub_context) { + JS_ReportOutOfMemory(context); + return JS_FALSE; + } + +#ifdef USE_JS_SETOPCB + JS_SetContextThread(sub_context); + JS_BeginRequest(sub_context); +#endif + + src = JS_GetStringChars(str); + srclen = JS_GetStringLength(str); + + if (!sandbox) { + sandbox = JS_NewObject(sub_context, NULL, NULL, NULL); + if (!sandbox || !JS_InitStandardClasses(sub_context, sandbox)) { + ok = JS_FALSE; + goto out; + } + } + + if (srclen == 0) { + *rval = OBJECT_TO_JSVAL(sandbox); + ok = JS_TRUE; + } else { + ok = JS_EvaluateUCScript(sub_context, sandbox, src, srclen, NULL, 0, + rval); + ok = JS_TRUE; + } + +out: +#ifdef USE_JS_SETOPCB + JS_EndRequest(sub_context); + JS_ClearContextThread(sub_context); +#endif + + JS_DestroyContext(sub_context); + return ok; +} + +static JSBool +GC(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + JS_GC(context); + return JS_TRUE; +} + +static JSBool +Print(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + uintN i; + size_t cl, bl; + JSString *str; + jschar *chars; + char *bytes; + + for (i = 0; i < argc; i++) { + str = JS_ValueToString(context, argv[i]); + if (!str) + return JS_FALSE; + chars = JS_GetStringChars(str); + cl = JS_GetStringLength(str); + if (!EncodeString(chars, cl, NULL, &bl)) + return JS_FALSE; + bytes = JS_malloc(context, bl + 1); + bytes[bl] = '\0'; + if (!EncodeString(chars, cl, bytes, &bl)) { + JS_free(context, bytes); + return JS_FALSE; + } + fprintf(stdout, "%s%s", i ? " " : "", bytes); + JS_free(context, bytes); + } + + fputc('\n', stdout); + fflush(stdout); + return JS_TRUE; +} + +static JSBool +Quit(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + JS_ConvertArguments(context, argc, argv, "/ i", &gExitCode); + return JS_FALSE; +} + +static JSBool +ReadLine(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + char *bytes, *tmp; + jschar *chars; + size_t bufsize, byteslen, charslen, readlen; + JSString *str; + + JS_MaybeGC(context); + + byteslen = 0; + bufsize = 256; + bytes = JS_malloc(context, bufsize); + if (!bytes) + return JS_FALSE; + + while ((readlen = js_fgets(bytes + byteslen, bufsize - byteslen, stdin)) > 0) { + byteslen += readlen; + + /* Are we done? */ + if (bytes[byteslen - 1] == '\n') { + bytes[byteslen - 1] = '\0'; + break; + } + + /* Else, grow our buffer for another pass */ + tmp = JS_realloc(context, bytes, bufsize * 2); + if (!tmp) { + JS_free(context, bytes); + return JS_FALSE; + } + + bufsize *= 2; + bytes = tmp; + } + + /* Treat the empty string specially */ + if (byteslen == 0) { + *rval = JS_GetEmptyStringValue(context); + JS_free(context, bytes); + return JS_TRUE; + } + + /* Shrink the buffer to the real size */ + tmp = JS_realloc(context, bytes, byteslen); + if (!tmp) { + JS_free(context, bytes); + return JS_FALSE; + } + bytes = tmp; + + /* Decode the string from UTF-8 */ + if (!DecodeString(bytes, byteslen, NULL, &charslen)) { + JS_free(context, bytes); + return JS_FALSE; + } + chars = JS_malloc(context, (charslen + 1) * sizeof(jschar)); + if (!DecodeString(bytes, byteslen, chars, &charslen)) { + JS_free(context, bytes); + JS_free(context, chars); + return JS_FALSE; + } + JS_free(context, bytes); + chars[charslen] = '\0'; + + /* Initialize a JSString object */ + str = JS_NewUCString(context, chars, charslen - 1); + if (!str) { + JS_free(context, chars); + return JS_FALSE; + } + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +Seal(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + JSObject *target; + JSBool deep = JS_FALSE; + + if (!JS_ConvertArguments(context, argc, argv, "o/b", &target, &deep)) + return JS_FALSE; + if (!target) + return JS_TRUE; + return JS_SealObject(context, target, deep); +} + +static void +ExecuteScript(JSContext *context, JSObject *obj, const char *filename) { + FILE *file; + JSScript *script; + jsval result; + + if (!filename || strcmp(filename, "-") == 0) { + file = stdin; + } else { + file = fopen(filename, "r"); + if (!file) { + fprintf(stderr, "could not open script file %s\n", filename); + gExitCode = 1; + return; + } + } + + script = JS_CompileFileHandle(context, obj, filename, file); + if (script) { + JS_ExecuteScript(context, obj, script, &result); + JS_DestroyScript(context, script); + } +} + +static uint32 gBranchCount = 0; + +#ifdef USE_JS_SETOPCB +static JSBool +OperationCallback(JSContext *context) +{ + if ((++gBranchCount & 0x3fff) == 1) { + JS_MaybeGC(context); + } + return JS_TRUE; +} +#else +static JSBool +BranchCallback(JSContext *context, JSScript *script) { + if ((++gBranchCount & 0x3fff) == 1) { + JS_MaybeGC(context); + } + return JS_TRUE; +} +#endif + +static void +PrintError(JSContext *context, const char *message, JSErrorReport *report) { + if (!report || !JSREPORT_IS_WARNING(report->flags)) + fprintf(stderr, "%s\n", message); +} + +JSBool ThrowError(JSContext *cx, const char *message) +{ + void *mark; + jsval *args; + jsval exc; + + printf("%s\n",message); + + args = JS_PushArguments(cx, &mark, "s", message); + if (args) { + if (JS_CallFunctionName(cx, JS_GetGlobalObject(cx), + "Error", 1, args, &exc)) + JS_SetPendingException(cx, exc); + JS_PopArguments(cx, mark); + } + + return JS_FALSE; +} + +typedef struct buffer_counter { + Buffer buffer; + int pos; +}* BufferCount; + +size_t curl_read(void *ptr, size_t size, size_t nmemb, void *stream) { + int readlength, spaceleft, i; + char* databuffer = (char*)ptr; + Buffer b = ((BufferCount)stream)->buffer; + int* pos = &(((BufferCount)stream)->pos); + + if( size == 0 || nmemb == 0) { + return 0; + } + + if((b->count - *pos) == 0) { + return 0; + } + + readlength = size*nmemb; + spaceleft = b->count - *pos; + + if(readlength < spaceleft) { + copy_Buffer(b,databuffer,*pos,readlength); + *(pos) += readlength; + return readlength; + } else { + copy_Buffer(b,databuffer,*pos,spaceleft); + *(pos) += spaceleft; + return spaceleft; + } +} + +size_t curl_write(void *ptr, size_t size, size_t nmemb, void *stream) { + char *data, *tmp; + Buffer b; + if( size == 0 || nmemb == 0 ) + return 0; + + data = (char *)ptr; + b = (Buffer)stream; + + append_Buffer(b,data,size*nmemb); + + return size*nmemb; +} + +// This uses MALLOC dont forget to free +char* JSValToChar(JSContext* context, jsval* arg) { + char *c, *tmp; + JSString *jsmsg; + size_t len; + int i; + if(!JSVAL_IS_STRING(*arg)) { + return NULL; + } + + jsmsg = JS_ValueToString(context,*arg); + len = JS_GetStringLength(jsmsg); + tmp = JS_GetStringBytes(jsmsg); + + c = (char*)malloc(len+1); + c[len] = '\0'; + + for(i = 0;i < len;i++) { + c[i] = tmp[i]; + } + + return c; +} + +JSBool BufferToJSVal(JSContext *context, Buffer b, jsval *rval) { + char* c; + JSString *str; + + // Important for char* to be JS_malloced, otherwise js wont let you use it in the NewString method + c = JS_malloc(context, b->count * sizeof(char)); + copy_Buffer(b,c,0,b->count); + + + /* Initialize a JSString object */ + str = JS_NewString(context, c, b->count); + + if (!str) { + JS_free(context, c); + return JS_FALSE; + } + + // Set Return Value + *rval = STRING_TO_JSVAL(str); + if(rval == NULL) { + return JS_FALSE; + } + return JS_TRUE; +} + +struct curl_slist* generateCurlHeaders(JSContext* context,jsval* arg) { + // If arg is an object then we go the header-hash route else return NULL + + if(!JSVAL_IS_NULL(*arg)) { + + struct curl_slist *slist = NULL; + JSObject* header_obj; + JSObject* iterator; + jsval *jsProperty; + jsval *jsValue; + jsid *jsId; + Buffer bTmp; + char* jsPropertyName, *jsPropertyValue; + + // If we fail to convert arg2 to an object. Error! + if(!JS_ValueToObject(context,*arg,&header_obj)) { + return NULL; + } + + iterator = JS_NewPropertyIterator(context,header_obj); + + jsProperty = JS_malloc(context,sizeof(jsval)); + jsValue = JS_malloc(context,sizeof(jsval)); + jsId = JS_malloc(context,sizeof(jsid)); + + while(JS_NextProperty(context,iterator,jsId) == JS_TRUE) { + + if(*jsId == JSVAL_VOID) { + break; + } + + // TODO: Refactor this maybe make a JSValAppendBuffer method b/c that is what you really want to do. + + bTmp = init_Buffer(); + JS_IdToValue(context,*jsId,jsProperty); + jsPropertyName = JSValToChar(context,jsProperty); + + // TODO: Remove strlen =/ + append_Buffer(bTmp,jsPropertyName,strlen(jsPropertyName)); + append_Buffer(bTmp,": ",2); + + JS_GetProperty(context,header_obj,jsPropertyName,jsValue); + jsPropertyValue = JSValToChar(context,jsValue); + // TODO: Remove strlen =/ + append_Buffer(bTmp,jsPropertyValue,strlen(jsPropertyValue)); + append_Buffer(bTmp,"",1); + + slist = curl_slist_append(slist,bTmp->data); + + free_Buffer(bTmp); + free(jsPropertyValue); + free(jsPropertyName); + } + + JS_free(context,jsProperty); + JS_free(context,jsValue); + JS_free(context,jsId); + + return slist; + + } else { + return NULL; + } +} + +static JSBool +GetHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + CURL* handle; + Buffer b; + char *url; + size_t charslen, readlen; + struct curl_slist *slist; + int exitcode; + + // Run GC + JS_MaybeGC(context); + + // Init Curl + if((handle = curl_easy_init()) == NULL) { + return JS_FALSE; + } + + // Get URL + url = JSValToChar(context,argv); + if( url == NULL ) { + return ThrowError(context,"Unable to convert url (argument 0) to a string"); + } + + b = init_Buffer(); // Allocate buffer that will store the get resultant + + // Configuration + curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEDATA,b); + curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b); + curl_easy_setopt(handle,CURLOPT_URL,url); + curl_easy_setopt(handle,CURLOPT_HTTPGET,1); + curl_easy_setopt(handle,CURLOPT_FOLLOWLOCATION,1); + curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4); + + slist = generateCurlHeaders(context,argv+1); + if(slist != NULL) { + curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist); + } + + // Perform + if((exitcode = curl_easy_perform(handle)) != 0) { + if(slist != NULL) { + curl_slist_free_all(slist); + } + curl_easy_cleanup(handle); + free(url); + free_Buffer(b); + return JS_FALSE; + } + + free(url); + if(slist != NULL) { + curl_slist_free_all(slist); + } + + /* Treat the empty string specially */ + if (b->count == 0) { + free_Buffer(b); + *rval = JS_GetEmptyStringValue(context); + curl_easy_cleanup(handle); + return JS_TRUE; + } + + /* Shrink the buffer to the real size and store its value in rval */ + shrink_Buffer(b); + BufferToJSVal(context,b,rval); + + // Free Buffer + free_Buffer(b); + + if(rval == NULL) { + curl_easy_cleanup(handle); + return JS_FALSE; + } + + JS_MaybeGC(context); + + curl_easy_cleanup(handle); + + return JS_TRUE; +} + +static JSBool +HeadHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + CURL* handle; + Buffer b; + char *url; + size_t charslen, readlen; + struct curl_slist *slist; + int exitcode; + + // Run GC + JS_MaybeGC(context); + + // Init Curl + if((handle = curl_easy_init()) == NULL) { + return JS_FALSE; + } + + // Get URL + url = JSValToChar(context,argv); + if( url == NULL ) { + return ThrowError(context,"Unable to convert url (argument 0) to a string"); + } + + b = init_Buffer(); // Allocate buffer that will store the get resultant + + // Configuration + // curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write); + // curl_easy_setopt(handle,CURLOPT_WRITEDATA,b); + curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b); + curl_easy_setopt(handle,CURLOPT_URL,url); + curl_easy_setopt(handle,CURLOPT_HTTPGET,0); + curl_easy_setopt(handle,CURLOPT_NOBODY,1); + curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4); + + slist = generateCurlHeaders(context,argv+1); + if(slist != NULL) { + curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist); + } + + // fprintf(stderr, "about to run HEAD request\n"); + + // Perform + if((exitcode = curl_easy_perform(handle)) != 0) { + if(slist != NULL) { + curl_slist_free_all(slist); + } + curl_easy_cleanup(handle); + free(url); + free_Buffer(b); + return JS_FALSE; + } + // fprintf(stderr, "ran ok HEAD request\n"); + + free(url); + if(slist != NULL) { + curl_slist_free_all(slist); + } + + /* Treat the empty string specially */ + if (b->count == 0) { + free_Buffer(b); + *rval = JS_GetEmptyStringValue(context); + curl_easy_cleanup(handle); + return JS_TRUE; + } + + /* Shrink the buffer to the real size and store its value in rval */ + shrink_Buffer(b); + BufferToJSVal(context,b,rval); + + // Free Buffer + free_Buffer(b); + + if(rval == NULL) { + curl_easy_cleanup(handle); + return JS_FALSE; + } + + JS_MaybeGC(context); + + curl_easy_cleanup(handle); + + return JS_TRUE; +} + + +static JSBool +PostHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + CURL* handle; + Buffer b; + char *url, *body; + size_t charslen, readlen; + struct curl_slist *slist; + int exitcode; + + // Run GC + JS_MaybeGC(context); + + // Init Curl + if((handle = curl_easy_init()) == NULL) { + return JS_FALSE; + } + + // Get URL + if((url = JSValToChar(context,argv)) == NULL) { + curl_easy_cleanup(handle); + return JS_FALSE; + } + + // Initialize buffer + b = init_Buffer(); + + curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write); // function that recieves data + curl_easy_setopt(handle,CURLOPT_WRITEDATA,b); // buffer to write the data to + curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b); + curl_easy_setopt(handle,CURLOPT_URL,url); // url + curl_easy_setopt(handle,CURLOPT_POST,1); // Set Op. to post + curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1); // No Progress Meter + curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4); // only ipv4 + + if((body = JSValToChar(context,argv+1)) == NULL) { // Convert arg1 to a string + free(url); + free_Buffer(b); + curl_easy_cleanup(handle); + return JS_FALSE; + } + + curl_easy_setopt(handle,CURLOPT_POSTFIELDSIZE,strlen(body)); + curl_easy_setopt(handle,CURLOPT_POSTFIELDS,body); // Curl wants '\0' terminated, we oblige + + slist = generateCurlHeaders(context,argv+2); // Initialize Headers + if(slist != NULL) { + curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist); + } + + if((exitcode = curl_easy_perform(handle)) != 0) { // Perform + curl_slist_free_all(slist); + free(body); + free(url); + free_Buffer(b); + curl_easy_cleanup(handle); + return JS_FALSE; + } + + free(body); + free(url); + curl_slist_free_all(slist); + + // Convert response back to javascript value and then clean + BufferToJSVal(context,b,rval); + free_Buffer(b); + curl_easy_cleanup(handle); + + JS_MaybeGC(context); + + if( rval == NULL ) { + return JS_FALSE; + } + + return JS_TRUE; +} + +#define CLEAN \ + free_Buffer(b); \ + free_Buffer(b_data->buffer); \ + free(b_data); \ + free(url) + +static JSBool +PutHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ + + Buffer b; + BufferCount b_data; + char *url, *data; + size_t charslen, readlen; + JSObject* header_obj; + CURL* handle; + struct curl_slist *slist; + int exitcode; + + // Run GC + JS_MaybeGC(context); + + // Get URL + url = JSValToChar(context,argv); + + // Allocate buffer that will store the get resultant + b = init_Buffer(); + + // Allocate data buffer and move data into them + b_data = (BufferCount)malloc(sizeof(Buffer) + sizeof(int)); + b_data->buffer = init_Buffer(); + b_data->pos = 0; + + data = JSValToChar(context,(argv+1)); + readlen = strlen(data); + + + + // TODO: remove strlen + append_Buffer(b_data->buffer,data,readlen); + + free(data); + + // Init Curl + + if((handle = curl_easy_init()) == NULL) { + CLEAN; + return JS_FALSE; + } + + // Configuration + curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEDATA,b); + curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b); + curl_easy_setopt(handle,CURLOPT_READFUNCTION,curl_read); + curl_easy_setopt(handle,CURLOPT_READDATA,b_data); + curl_easy_setopt(handle,CURLOPT_URL,url); + curl_easy_setopt(handle,CURLOPT_UPLOAD,1); + curl_easy_setopt(handle,CURLOPT_INFILESIZE,readlen); + + + + // Curl structure + slist = generateCurlHeaders(context,argv+2); + if(slist != NULL) { + curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist); + } + + // Little Things + // No progress meter + curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1); + // Use only ipv4 + curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4); + + + + // Perform + if((exitcode = curl_easy_perform(handle)) != 0) { + if(slist != NULL) + curl_slist_free_all(slist); + curl_easy_cleanup(handle); + CLEAN; + return JS_FALSE; + } + + if(slist != NULL) + curl_slist_free_all(slist); + free_Buffer(b_data->buffer); + free(b_data); + free(url); + + /* Treat the empty string specially */ + if (b->count == 0) { + *rval = JS_GetEmptyStringValue(context); + curl_easy_cleanup(handle); + free_Buffer(b); + return JS_TRUE; + } + + /* Shrink the buffer to the real size */ + shrink_Buffer(b); + + BufferToJSVal(context,b,rval); + + free_Buffer(b); + + if(rval == NULL) { + curl_easy_cleanup(handle); + return JS_FALSE; + } + + JS_MaybeGC(context); + + curl_easy_cleanup(handle); + + return JS_TRUE; +} + +static JSBool +DelHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + Buffer b; + char *url; + size_t charslen, readlen; + char header_name[7]; + CURL* handle; + int exitcode; + struct curl_slist *slist = NULL; + + strcpy(header_name,"DELETE"); + + // Run GC + JS_MaybeGC(context); + + // Get URL + url = JSValToChar(context,argv); + + // Allocate buffer that will store the del resultant + b = init_Buffer(); + + // Init Curl + if((handle = curl_easy_init()) == NULL) { + free_Buffer(b); + return JS_FALSE; + } + + // Configuration + curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEDATA,b); + curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b); + curl_easy_setopt(handle,CURLOPT_URL,url); + curl_easy_setopt(handle,CURLOPT_CUSTOMREQUEST,header_name); + curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4); + + // Curl structure + if((slist = generateCurlHeaders(context,argv+1)) != NULL) { + curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist); + } + + // Perform + if((exitcode = curl_easy_perform(handle)) != 0) { + if(slist != NULL) + curl_slist_free_all(slist); + curl_easy_cleanup(handle); + free(url); + free_Buffer(b); + return JS_FALSE; + } + + if(slist != NULL) + curl_slist_free_all(slist); + free(url); + + /* Treat the empty string specially */ + if (b->count == 0) { + *rval = JS_GetEmptyStringValue(context); + curl_easy_cleanup(handle); + free_Buffer(b); + return JS_TRUE; + } + + /* Shrink the buffer to the real size */ + shrink_Buffer(b); + + BufferToJSVal(context,b,rval); + + if(rval == NULL) { + curl_easy_cleanup(handle); + return JS_FALSE; + } + + JS_MaybeGC(context); + + curl_easy_cleanup(handle); + + return JS_TRUE; +} + +static JSBool +CopyHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + Buffer b; + char *url; + size_t charslen, readlen; + char header_name[5]; + CURL* handle; + int exitcode; + struct curl_slist *slist = NULL; + + strcpy(header_name,"COPY"); + + // Run GC + JS_MaybeGC(context); + + // Get URL + url = JSValToChar(context,argv); + + // Allocate buffer that will store the del resultant + b = init_Buffer(); + + // Init Curl + if((handle = curl_easy_init()) == NULL) { + free_Buffer(b); + return JS_FALSE; + } + + // Configuration + curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEDATA,b); + curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b); + curl_easy_setopt(handle,CURLOPT_URL,url); + curl_easy_setopt(handle,CURLOPT_CUSTOMREQUEST,header_name); + curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4); + + // Curl structure + if((slist = generateCurlHeaders(context,argv+1)) != NULL) { + curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist); + } + + // Perform + if((exitcode = curl_easy_perform(handle)) != 0) { + if(slist != NULL) + curl_slist_free_all(slist); + curl_easy_cleanup(handle); + free(url); + free_Buffer(b); + return JS_FALSE; + } + + if(slist != NULL) + curl_slist_free_all(slist); + free(url); + + /* Treat the empty string specially */ + if (b->count == 0) { + *rval = JS_GetEmptyStringValue(context); + curl_easy_cleanup(handle); + free_Buffer(b); + return JS_TRUE; + } + + /* Shrink the buffer to the real size */ + shrink_Buffer(b); + + BufferToJSVal(context,b,rval); + + if(rval == NULL) { + curl_easy_cleanup(handle); + return JS_FALSE; + } + + JS_MaybeGC(context); + + curl_easy_cleanup(handle); + + return JS_TRUE; +} + +static JSBool +MoveHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { + Buffer b; + char *url; + size_t charslen, readlen; + char header_name[5]; + CURL* handle; + struct curl_slist *slist = NULL; + int exitcode; + + strcpy(header_name,"MOVE"); + + // Run GC + JS_MaybeGC(context); + + // Get URL + url = JSValToChar(context,argv); + + // Allocate buffer that will store the del resultant + b = init_Buffer(); + + // Init Curl + if((handle = curl_easy_init()) == NULL) { + free_Buffer(b); + return JS_FALSE; + } + + // Configuration + curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEDATA,b); + curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write); + curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b); + curl_easy_setopt(handle,CURLOPT_URL,url); + curl_easy_setopt(handle,CURLOPT_CUSTOMREQUEST,header_name); + curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1); + curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4); + + // Curl structure + if((slist = generateCurlHeaders(context,argv+1)) != NULL) { + curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist); + } + + // Perform + if((exitcode = curl_easy_perform(handle)) != 0) { + if(slist != NULL) + curl_slist_free_all(slist); + curl_easy_cleanup(handle); + free(url); + free_Buffer(b); + return JS_FALSE; + } + + if(slist != NULL) + curl_slist_free_all(slist); + free(url); + + /* Treat the empty string specially */ + if (b->count == 0) { + *rval = JS_GetEmptyStringValue(context); + curl_easy_cleanup(handle); + free_Buffer(b); + return JS_TRUE; + } + + /* Shrink the buffer to the real size */ + shrink_Buffer(b); + + BufferToJSVal(context,b,rval); + + if(rval == NULL) { + curl_easy_cleanup(handle); + return JS_FALSE; + } + + JS_MaybeGC(context); + + curl_easy_cleanup(handle); + + return JS_TRUE; +} + +int +main(int argc, const char * argv[]) { + JSRuntime *runtime; + JSContext *context; + JSObject *global; + + runtime = JS_NewRuntime(64L * 1024L * 1024L); + if (!runtime) + return 1; + context = JS_NewContext(runtime, gStackChunkSize); + if (!context) + return 1; + /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=477187 */ + JS_SetErrorReporter(context, PrintError); +#ifdef USE_JS_SETOPCB + JS_SetContextThread(context); + JS_BeginRequest(context); + JS_SetOperationCallback(context, OperationCallback); +#else + JS_SetBranchCallback(context, BranchCallback); + JS_ToggleOptions(context, JSOPTION_NATIVE_BRANCH_CALLBACK); +#endif + JS_ToggleOptions(context, JSOPTION_XML); + + global = JS_NewObject(context, NULL, NULL, NULL); + if (!global) + return 1; + if (!JS_InitStandardClasses(context, global)) + return 1; + if (!JS_DefineFunction(context, global, "evalcx", EvalInContext, 0, 0) + || !JS_DefineFunction(context, global, "gc", GC, 0, 0) + || !JS_DefineFunction(context, global, "print", Print, 0, 0) + || !JS_DefineFunction(context, global, "quit", Quit, 0, 0) + || !JS_DefineFunction(context, global, "readline", ReadLine, 0, 0) + || !JS_DefineFunction(context, global, "seal", Seal, 0, 0) + || !JS_DefineFunction(context, global, "gethttp", GetHttp, 1, 0) + || !JS_DefineFunction(context, global, "headhttp", HeadHttp, 1, 0) + || !JS_DefineFunction(context, global, "posthttp", PostHttp, 2, 0) + || !JS_DefineFunction(context, global, "puthttp", PutHttp, 2, 0) + || !JS_DefineFunction(context, global, "delhttp", DelHttp, 1, 0) + || !JS_DefineFunction(context, global, "movehttp", MoveHttp, 1, 0) + || !JS_DefineFunction(context, global, "copyhttp", CopyHttp, 1, 0)) + return 1; + + if (argc != 2) { + fprintf(stderr, "incorrect number of arguments\n\n"); + fprintf(stderr, "usage: %s <scriptfile>\n", argv[0]); + return 2; + } + + ExecuteScript(context, global, argv[1]); + +#ifdef USE_JS_SETOPCB + JS_EndRequest(context); + JS_ClearContextThread(context); +#endif + + JS_DestroyContext(context); + JS_DestroyRuntime(runtime); + JS_ShutDown(); + + return gExitCode; +} diff --git a/src/couchdb/priv/couch_js/curlhelper.c b/src/couchdb/priv/couch_js/curlhelper.c 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 <stdlib.h> +#include <stdio.h> +#include "curlhelper.h" + +#define TRUE 1 +#define FALSE 0 + +Buffer init_Buffer() { + Buffer b; + + if((b = (Buffer)malloc(sizeof(char*) + sizeof(int)*2)) == NULL) { + return NULL; + } + + b->count = 0; + b->capacity = 50; + + if(b->data = (char*)malloc(sizeof(char)*b->capacity)) { + return b; + } else { + return NULL; + } +} + +void free_Buffer(Buffer b) { + if(b == NULL) + return; + if(b->data != NULL) + free(b->data); + free(b); +} + +int append_Buffer(Buffer b, char* c, int length) { + int capacity_changed; + int new_count; + int i; + + capacity_changed = FALSE; + new_count = b->count + length; + + if(new_count > b->capacity) { + capacity_changed = TRUE; + b->capacity *= 2; + } + + while(new_count > b->capacity) { + b->capacity *= 2; + } + + if(capacity_changed) { + if((b->data = (char*)realloc(b->data,b->capacity)) == NULL) { + return FALSE; + } + } + + for(i = 0;i < length;i++) { + *(b->data + b->count + i) = *(c + i); + } + + b->count = new_count; + + return TRUE; +} + +char* toString_Buffer(Buffer b) { + char* result; + int i; + + if((result = (char*)malloc(sizeof(char)*(b->count+1))) == NULL) { + return NULL; + } + + result[b->count] = '\0'; + + for(i = 0;i < b->count;i++) { + result[i] = b->data[i]; + } + + return result; +} + +int print_Buffer(Buffer b) { + char* c; + + if((c = toString_Buffer(b)) == NULL) { + return FALSE; + } + + printf("%s\n",c); + + free(c); + + return TRUE; +} + + +int shrink_Buffer(Buffer b) { + b->capacity = b->count; + + if((b->data = realloc(b->data,sizeof(char)*b->capacity)) == NULL) { + return FALSE; + } else { + return TRUE; + } +} + +void copy_Buffer(Buffer b, char* c, int pos, int length) { + int i; + if((pos + length) > b->count) + return; + + for(i = 0; i < length;i++) { + *(c + i) = *(b->data + pos + i); + } +} + + +List init_List(int capacity) { + List l; + if(capacity < 5) + capacity = 5; + + if((l = (List)malloc(sizeof(void**)+sizeof(int)*2)) == NULL) { + return NULL; + } + + l->count = 0; + l->capacity = capacity; + + if((l->elements = (void**)malloc(sizeof(void*)*l->capacity)) == NULL) { + return NULL; + } + + return l; +} + +void free_List(List l) { + if(l == NULL) + return; + if(l->elements != NULL) + free(l->elements); + free(l); +} + +void* get_List(List l, int pos) { + if(pos > (l->count - 1)) { + return NULL; + } + return *(l->elements + pos); +} + +void* pull_List(List l) { + void* r = *(l->elements); + + int i; + + for(i = 1; i < (l->count-1);i++) { + l->elements[i] = l->elements[i+1]; + } + l->count -= 1; + return r; +} + +int set_List(List l, int pos, void* ptr) { + if(pos > (l->count - 1)) { + return FALSE; + } + + *(l->elements + pos) = ptr; + + return TRUE; +} + +int append_List(List l, void* ptr, int length) { + int capacity_changed; + int new_count; + int i; + + capacity_changed = FALSE; + new_count = l->count + length; + + if(new_count > l->capacity) { + capacity_changed = TRUE; + l->capacity *= 2; + } + + while(new_count > l->capacity) { + l->capacity *= 2; + } + + if(capacity_changed) { + if((l->elements = (void*)realloc(l->elements,l->capacity)) == NULL) { + return FALSE; + } + } + + for(i = 0;i < length;i++) { + *(l->elements + l->count + i) = (void *)((char *)ptr + i); + } + + l->count = new_count; + + return TRUE; +} + +int push_List(List l, void* ptr, int length) { + int capacity_changed; + int new_count; + int i; + + capacity_changed = FALSE; + new_count = l->count + length; + + if(new_count > l->capacity) { + capacity_changed = TRUE; + l->capacity *= 2; + } + + while(new_count > l->capacity) { + l->capacity *= 2; + } + + if(capacity_changed) { + if((l->elements = (void*)realloc(l->elements,l->capacity)) == NULL) { + return FALSE; + } + } + + for(i = 0;i < length;i++) { + *(l->elements + l->count + i) = *(l->elements + i); + } + + for(i = 0;i < length;i++) { + *(l->elements + i) = (void *)((char *)ptr+i); + } + + l->count = new_count; + + return TRUE; +} diff --git a/src/couchdb/priv/couch_js/curlhelper.h b/src/couchdb/priv/couch_js/curlhelper.h 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 |