Esempio n. 1
0
static JSBool
sendreq(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
{
    HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
    char* body = NULL;
    size_t bodylen = 0;
    JSBool ret = JS_FALSE;

    if(!http)
    {
        JS_ReportError(cx, "Invalid CouchHTTP instance.");
        goto done;
    }

    if(argv[0] != JSVAL_VOID && argv[0] != JS_GetEmptyStringValue(cx))
    {
        body = enc_string(cx, argv[0], &bodylen);
        if(!body)
        {
            JS_ReportError(cx, "Failed to encode body.");
            goto done;
        }
    }

    ret = go(cx, obj, http, body, bodylen);

done:
    if(body) free(body);
    return ret;
}
Esempio n. 2
0
JSBool
Output_initialize (JSContext* cx)
{
    JSBool result = JS_FALSE;

    JS_BeginRequest(cx);
    JS_EnterLocalRootScope(cx);

    jsval property;

    JSObject* output = JS_DefineObject(cx, ((LCGIData*) JS_GetContextPrivate(cx))->global, 
        "Output", &Output_class, NULL, 0
    );

    if (output) {
        property = INT_TO_JSVAL(1024);
        JS_SetProperty(cx, output, "limit", &property);
        property = JSVAL_FALSE;
        JS_SetProperty(cx, output, "buffered", &property);
        property = JS_GetEmptyStringValue(cx);
        JS_SetProperty(cx, output, "content", &property);

        result = JS_TRUE;
    }
    
    JS_LeaveLocalRootScope(cx);
    JS_EndRequest(cx);

    return result;
}
Esempio n. 3
0
// convert a readable to a JSString, copying string data
// static
jsval
XPCStringConvert::ReadableToJSVal(JSContext *cx,
                                  const nsAString &readable,
                                  nsStringBuffer** sharedBuffer)
{
    JSString *str;
    *sharedBuffer = nullptr;

    uint32_t length = readable.Length();

    if (length == 0)
        return JS_GetEmptyStringValue(cx);

    nsStringBuffer *buf = nsStringBuffer::FromString(readable);
    if (buf) {
        if (buf == sCachedBuffer &&
            js::GetGCThingCompartment(sCachedString) == js::GetContextCompartment(cx)) {
            // We're done.  Just return our existing string.
            return JS::StringValue(sCachedString);
        }

        // yay, we can share the string's buffer!

        str = JS_NewExternalString(cx,
                                   reinterpret_cast<jschar *>(buf->Data()),
                                   length, &sDOMStringFinalizer);

        if (str) {
            *sharedBuffer = buf;
            sCachedString = str;
            sCachedBuffer = buf;
        }
    } else {
        // blech, have to copy.

        jschar *chars = reinterpret_cast<jschar *>
                                        (JS_malloc(cx, (length + 1) *
                                                   sizeof(jschar)));
        if (!chars)
            return JSVAL_NULL;

        if (length && !CopyUnicodeTo(readable, 0,
                                     reinterpret_cast<PRUnichar *>(chars),
                                     length)) {
            JS_free(cx, chars);
            return JSVAL_NULL;
        }

        chars[length] = 0;

        str = JS_NewUCString(cx, chars, length);
        if (!str)
            JS_free(cx, chars);
    }
    return str ? STRING_TO_JSVAL(str) : JSVAL_NULL;
}
Esempio n. 4
0
void FFSessionHandler::getStringObjectClass(JSContext* ctx) {
  jsval str = JS_GetEmptyStringValue(ctx);
  JSObject* obj = 0;
  if (!JS_ValueToObject(ctx, str, &obj)) {
    return;
  }
  if (!obj) {
    return;
  }
  stringObjectClass = JS_GET_CLASS(ctx, obj);
}
// ****************************************************************************
// ***  static helper functions
// ****************************************************************************
JSBool GetValue(JSContext *cx, JSObject *obj, int index, jsval *ret) {
  gstRecordJSWrapperImpl* wrapper =
    static_cast<gstRecordJSWrapperImpl*>(JS_GetPrivate(cx, obj));
  if (!wrapper) {
    JS_ReportError(cx, "object doesn't have gstRecordWrapper\n");
    return JS_FALSE;
  }

  const gstHeaderHandle &header = wrapper->header();
  const gstRecordHandle &rec    = wrapper->record();
  if (!rec) {
    JS_ReportError(cx, "gstRecordWrapper doesn't have an active record");
    return JS_FALSE;
  }


  if ((index >= 0) && (index < (int)header->numColumns())) {
    switch (header->ftype(index)) {
      case gstTagBoolean:
        *ret = BOOLEAN_TO_JSVAL(rec->Field(index)->ValueAsBoolean());
        return JS_TRUE;
      case gstTagInt:
      case gstTagUInt:
      case gstTagInt64:
      case gstTagUInt64:
      case gstTagFloat:
      case gstTagDouble:
        return JS_NewNumberValue(cx, rec->Field(index)->ValueAsDouble(), ret);
      case gstTagString:
      case gstTagUnicode:
        {
          QString str = rec->Field(index)->ValueAsUnicode();
          if (str.isEmpty()) {
            *ret = JS_GetEmptyStringValue(cx);
          } else {
            JSString *newstr = JS_NewUCStringCopyN(cx, str.ucs2(), str.length());
            if (!newstr) {
              // out of memory error already reported
              return JS_FALSE;
            }
            *ret = STRING_TO_JSVAL(newstr);
          }
          return JS_TRUE;
        }
      default:
        JS_ReportError(cx, "Unhandled field type: %d",
                       header->ftype(index));
    }
  } else {
    JS_ReportError(cx, "field index out of range: %d", index);
  }
  return JS_FALSE;
}
// convert a readable to a JSString, copying string data
// static
jsval
XPCStringConvert::ReadableToJSVal(JSContext *cx,
                                  const nsAString &readable,
                                  nsStringBuffer** sharedBuffer)
{
    JSString *str;
    *sharedBuffer = nullptr;

    uint32_t length = readable.Length();

    if (length == 0)
        return JS_GetEmptyStringValue(cx);

    nsStringBuffer *buf = nsStringBuffer::FromString(readable);
    if (buf) {
        JS::RootedValue val(cx);
        bool shared;
        bool ok = StringBufferToJSVal(cx, buf, length, &val, &shared);
        if (!ok) {
            return JS::NullValue();
        }

        if (shared) {
            *sharedBuffer = buf;
        }
        return val;
    }

    // blech, have to copy.

    jschar *chars = reinterpret_cast<jschar *>
                                    (JS_malloc(cx, (length + 1) *
                                               sizeof(jschar)));
    if (!chars)
        return JS::NullValue();

    if (length && !CopyUnicodeTo(readable, 0,
                                 reinterpret_cast<PRUnichar *>(chars),
                                 length)) {
        JS_free(cx, chars);
        return JS::NullValue();
    }

    chars[length] = 0;

    str = JS_NewUCString(cx, chars, length);
    if (!str) {
        JS_free(cx, chars);
    }

    return str ? STRING_TO_JSVAL(str) : JSVAL_NULL;
}
Esempio n. 7
0
JSString*
couch_readline(JSContext* cx, FILE* fp)
{
    JSString* str;
    char* bytes = NULL;
    char* tmp = NULL;
    size_t used = 0;
    size_t byteslen = 256;
    size_t readlen = 0;

    bytes = JS_malloc(cx, byteslen);
    if(bytes == NULL) return NULL;

    while((readlen = couch_fgets(bytes+used, byteslen-used, fp)) > 0) {
        used += readlen;

        if(bytes[used-1] == '\n') {
            bytes[used-1] = '\0';
            break;
        }

        // Double our buffer and read more.
        byteslen *= 2;
        tmp = JS_realloc(cx, bytes, byteslen);
        if(!tmp) {
            JS_free(cx, bytes);
            return NULL;
        }

        bytes = tmp;
    }

    // Treat empty strings specially
    if(used == 0) {
        JS_free(cx, bytes);
        return JSVAL_TO_STRING(JS_GetEmptyStringValue(cx));
    }

    // Shring the buffer to the actual data size
    tmp = JS_realloc(cx, bytes, used);
    if(!tmp) {
        JS_free(cx, bytes);
        return NULL;
    }
    bytes = tmp;
    byteslen = used;

    str = dec_string(cx, bytes, byteslen);
    JS_free(cx, bytes);
    return str;
}
Esempio n. 8
0
loc_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSURL *url;
    MochaDecoder *decoder;
    MWContext *context;
    const char *url_string;
    JSString *str;

    if (!JSVAL_IS_INT(id) || JSVAL_TO_INT(id) < 0)
        return JS_TRUE;

    url = JS_GetInstancePrivate(cx, obj, &lm_location_class, NULL);
    if (!url)
	return JS_TRUE;
    decoder = url->url_decoder;

    context = decoder->window_context;
    if (!context)
	return JS_TRUE;
    if (context->type == MWContextMail || 
        context->type == MWContextNews ||
        context->type == MWContextMailMsg || 
        context->type == MWContextNewsMsg) {
        /*
	 * Don't give out the location of a the mail folder to a script
         * embedded in a mail message. Just return the empty string.
         */
	url->href = JSVAL_TO_STRING(JS_GetEmptyStringValue(cx));
    } else {
        /*
         * Only need to check permissions for native getters
         */
        if (!lm_CheckPermissions(cx, obj, JSTARGET_UNIVERSAL_BROWSER_READ))
	    return JS_FALSE;
        url_string = get_url_string(cx, obj);
        if (url_string && (!url->href || XP_STRCMP(JS_GetStringBytes(url->href), 
                                                   url_string) != 0))
        {
	    str = JS_NewStringCopyZ(cx, url_string);
	    if (!str)
		return JS_FALSE;
	    url->href = str;
        }
    }
    return url_getProperty(cx, obj, id, vp);
}
Esempio n. 9
0
JSBool
Output_flush (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JS_BeginRequest(cx);
    JS_EnterLocalRootScope(cx);

    jsval property;
    JS_GetProperty(cx, obj, "content", &property);

    FCGX_PutS(JS_GetStringBytes(JS_ValueToString(cx, property)), ((LCGIData*) JS_GetContextPrivate(cx))->cgi->out);

    property = JS_GetEmptyStringValue(cx);
    JS_SetProperty(cx, obj, "content", &property);

    JS_LeaveLocalRootScope(cx);
    JS_EndRequest(cx);

    return JS_TRUE;
}
Esempio n. 10
0
static JSBool navigator_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
  FILE *fOut;
  JSString *str;
  size_t n, i;
  jschar *s;

CMNavigator* cmNav = JS_GetInstancePrivate(cx, obj, &navigator_class, NULL);

if (JSVAL_IS_INT(id)) {
      switch (JSVAL_TO_INT(id)) {
        case navigator_appversion:
		*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, cmNav->appVersion));
		break;
	default:
		*vp = JS_GetEmptyStringValue(cx);
	}
}
 return JS_TRUE;

}
Esempio n. 11
0
JSBool
Output_content_set (JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
{
    JS_BeginRequest(cx);
    JS_EnterLocalRootScope(cx);

    jsval     property;
    int       limit;
    JSBool    buffered;
    JSString* string;

    JS_GetProperty(cx, obj, "limit", &property);
    JS_ValueToInt32(cx, property, &limit);
    JS_GetProperty(cx, obj, "buffered", &property);
    JS_ValueToBoolean(cx, property, &buffered);
    string = JS_ValueToString(cx, *vp);

    if (!buffered && JS_GetStringLength(string) > limit) {
        if (!JSVAL_TO_BOOLEAN(JS_EVAL(cx, "Headers.sent"))) {
            JS_EVAL(cx, "Headers.send()");
        }

        FCGX_Stream* out       = ((LCGIData*) JS_GetContextPrivate(cx))->cgi->out;
        char*        cString   = JS_GetStringBytes(string);
        char         size[300] = {NULL};
        sprintf(size, "%x", strlen(cString));

        FCGX_FPrintF(out, "%s\r\n%s\r\n", size, cString);

        property = JS_GetEmptyStringValue(cx);
        JS_SetProperty(cx, obj, "content", &property);
    }

    JS_LeaveLocalRootScope(cx);
    JS_EndRequest(cx);

    return JS_TRUE;
}
Esempio n. 12
0
component_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSComponent *component;
    JSString *str;
    jsint slot;

    if (!JSVAL_IS_INT(id))
	return JS_TRUE;

    slot = JSVAL_TO_INT(id);

    component = JS_GetInstancePrivate(cx, obj, &lm_component_class, NULL);
    if (!component)
	return JS_TRUE;

    switch (slot) {
      case COMPONENT_NAME:
        str = component->name;
	if (str)
	    *vp = STRING_TO_JSVAL(str);
	else
	    *vp = JS_GetEmptyStringValue(cx);
        break;

      case COMPONENT_ACTIVE:
	*vp = JSVAL_FALSE;
	if (ET_moz_CallFunctionBool((ETBoolPtrFunc)component->active_callback, NULL))
	    *vp = JSVAL_TRUE;
        break;

      default:
	break;
    }

    return JS_TRUE;
}
Esempio n. 13
0
/*
 * function readline()
 * Provides a hook for scripts to read a line from stdin.
 */
static JSBool
ReadLine(JSContext *cx, uintN argc, jsval *vp)
{
#define BUFSIZE 256
    FILE *from;
    char *buf, *tmp;
    size_t bufsize, buflength, gotlength;
    JSBool sawNewline;
    JSString *str;

	SG_UNUSED(argc);

    from = stdin;
    buflength = 0;
    bufsize = BUFSIZE;
    buf = (char *) JS_malloc(cx, bufsize);
    if (!buf)
        return JS_FALSE;

    sawNewline = JS_FALSE;
    while ((gotlength =
            js_fgets(buf + buflength, (int)(bufsize - buflength), from)) > 0) {
        buflength += gotlength;

        /* Are we done? */
        if (buf[buflength - 1] == '\n') {
            buf[buflength - 1] = '\0';
            sawNewline = JS_TRUE;
            break;
        } else if (buflength < bufsize - 1) {
            break;
        }

        /* Else, grow our buffer for another pass. */
        bufsize *= 2;
        if (bufsize > buflength) {
            tmp = (char *) JS_realloc(cx, buf, bufsize);
        } else {
            JS_ReportOutOfMemory(cx);
            tmp = NULL;
        }

        if (!tmp) {
            JS_free(cx, buf);
            return JS_FALSE;
        }

        buf = tmp;
    }

    /* Treat the empty string specially. */
    if (buflength == 0) {
        *vp = feof(from) ? JSVAL_NULL : JS_GetEmptyStringValue(cx);
        JS_free(cx, buf);
        return JS_TRUE;
    }

    /* Shrink the buffer to the real size. */
    tmp = (char *) JS_realloc(cx, buf, buflength);
    if (!tmp) {
        JS_free(cx, buf);
        return JS_FALSE;
    }

    buf = tmp;

    /*
     * Turn buf into a JSString. Note that buflength includes the trailing null
     * character.
     */
    str = JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength);
    JS_free(cx, buf);
    if (!str)
        return JS_FALSE;

    *vp = STRING_TO_JSVAL(str);
    return JS_TRUE;
}
Esempio n. 14
0
static JSBool
array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
               jsval *rval, JSBool localeString)
{
    JSBool ok;
    jsval v;
    jsuint length, index;
    jschar *chars, *ochars;
    size_t nchars, growth, seplen, tmplen;
    const jschar *sepstr;
    JSString *str;
    JSHashEntry *he;
    JSObject *obj2;

    ok = js_GetLengthProperty(cx, obj, &length);
    if (!ok)
        return JS_FALSE;

    he = js_EnterSharpObject(cx, obj, NULL, &chars);
    if (!he)
        return JS_FALSE;
    if (literalize) {
        if (IS_SHARP(he)) {
#if JS_HAS_SHARP_VARS
            nchars = js_strlen(chars);
#else
            chars[0] = '[';
            chars[1] = ']';
            chars[2] = 0;
            nchars = 2;
#endif
            goto make_string;
        }

        /*
         * Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the
         * terminating 0.
         */
        growth = (1 + 3 + 1) * sizeof(jschar);
        if (!chars) {
            nchars = 0;
            chars = (jschar *) malloc(growth);
            if (!chars)
                goto done;
        } else {
            MAKE_SHARP(he);
            nchars = js_strlen(chars);
            chars = (jschar *)
                realloc((ochars = chars), nchars * sizeof(jschar) + growth);
            if (!chars) {
                free(ochars);
                goto done;
            }
        }
        chars[nchars++] = '[';
    } else {
        /*
         * Free any sharp variable definition in chars.  Normally, we would
         * MAKE_SHARP(he) so that only the first sharp variable annotation is
         * a definition, and all the rest are references, but in the current
         * case of (!literalize), we don't need chars at all.
         */
        if (chars)
            JS_free(cx, chars);
        chars = NULL;
        nchars = 0;

        /* Return the empty string on a cycle as well as on empty join. */
        if (IS_BUSY(he) || length == 0) {
            js_LeaveSharpObject(cx, NULL);
            *rval = JS_GetEmptyStringValue(cx);
            return ok;
        }

        /* Flag he as BUSY so we can distinguish a cycle from a join-point. */
        MAKE_BUSY(he);
    }
    sepstr = NULL;
    seplen = JSSTRING_LENGTH(sep);

    v = JSVAL_NULL;
    for (index = 0; index < length; index++) {
        ok = JS_GetElement(cx, obj, index, &v);
        if (!ok)
            goto done;

        if (!literalize && (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))) {
            str = cx->runtime->emptyString;
        } else {
            if (localeString) {
                if (!js_ValueToObject(cx, v, &obj2) ||
                    !js_TryMethod(cx, obj2,
                                  cx->runtime->atomState.toLocaleStringAtom,
                                  0, NULL, &v)) {
                    str = NULL;
                } else {
                    str = js_ValueToString(cx, v);
                }
            } else {
                str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v);
            }
            if (!str) {
                ok = JS_FALSE;
                goto done;
            }
        }

        /* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */
        growth = (nchars + (sepstr ? seplen : 0) +
                  JSSTRING_LENGTH(str) +
                  3 + 1) * sizeof(jschar);
        if (!chars) {
            chars = (jschar *) malloc(growth);
            if (!chars)
                goto done;
        } else {
            chars = (jschar *) realloc((ochars = chars), growth);
            if (!chars) {
                free(ochars);
                goto done;
            }
        }

        if (sepstr) {
            js_strncpy(&chars[nchars], sepstr, seplen);
            nchars += seplen;
        }
        sepstr = JSSTRING_CHARS(sep);

        tmplen = JSSTRING_LENGTH(str);
        js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen);
        nchars += tmplen;
    }

  done:
    if (literalize) {
        if (chars) {
            if (JSVAL_IS_VOID(v)) {
                chars[nchars++] = ',';
                chars[nchars++] = ' ';
            }
            chars[nchars++] = ']';
        }
    } else {
        CLEAR_BUSY(he);
    }
    js_LeaveSharpObject(cx, NULL);
    if (!ok) {
        if (chars)
            free(chars);
        return ok;
    }

  make_string:
    if (!chars) {
        JS_ReportOutOfMemory(cx);
        return JS_FALSE;
    }
    chars[nchars] = 0;
    str = js_NewString(cx, chars, nchars, 0);
    if (!str) {
        free(chars);
        return JS_FALSE;
    }
    *rval = STRING_TO_JSVAL(str);
    return JS_TRUE;
}
Esempio n. 15
0
static JSBool
go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
{
    CurlState state;
    char* referer;
    JSString* jsbody;
    JSBool ret = JS_FALSE;
    jsval tmp;
    
    state.cx = cx;
    state.http = http;
    
    state.sendbuf = body;
    state.sendlen = bodylen;
    state.sent = 0;
    state.sent_once = 0;

    state.recvbuf = NULL;
    state.recvlen = 0;
    state.read = 0;

    if(HTTP_HANDLE == NULL) {
        HTTP_HANDLE = curl_easy_init();
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_READFUNCTION, send_body);
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKFUNCTION,
                                        (curl_seek_callback) seek_body);
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_HEADERFUNCTION, recv_header);
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEFUNCTION, recv_body);
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOPROGRESS, 1);
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_ERRORBUFFER, ERRBUF);
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_COOKIEFILE, "");
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_USERAGENT,
                                            "CouchHTTP Client - Relax");
    }
    
    if(!HTTP_HANDLE) {
        JS_ReportError(cx, "Failed to initialize cURL handle.");
        goto done;
    }

    if(!JS_GetReservedSlot(cx, obj, 0, &tmp)) {
      JS_ReportError(cx, "Failed to readreserved slot.");
      goto done;
    }

    if(!(referer = enc_string(cx, tmp, NULL))) {
      JS_ReportError(cx, "Failed to encode referer.");
      goto done;
    }
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_REFERER, referer);
    free(referer);

    if(http->method < 0 || http->method > OPTIONS) {
        JS_ReportError(cx, "INTERNAL: Unknown method.");
        goto done;
    }

    curl_easy_setopt(HTTP_HANDLE, CURLOPT_CUSTOMREQUEST, METHODS[http->method]);
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 0);
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 0);
    
    if(http->method == HEAD) {
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 1);
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0);
    } else if(http->method == POST || http->method == PUT) {
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 1);
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0);
    }
    
    if(body && bodylen) {
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, bodylen);        
    } else {
        curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, 0);
    }

    // curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1);

    curl_easy_setopt(HTTP_HANDLE, CURLOPT_URL, http->url);
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_HTTPHEADER, http->req_headers);
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_READDATA, &state);
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKDATA, &state);
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEHEADER, &state);
    curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEDATA, &state);

    if(curl_easy_perform(HTTP_HANDLE) != 0) {
        JS_ReportError(cx, "Failed to execute HTTP request: %s", ERRBUF);
        goto done;
    }
    
    if(!state.resp_headers) {
        JS_ReportError(cx, "Failed to recieve HTTP headers.");
        goto done;
    }

    tmp = OBJECT_TO_JSVAL(state.resp_headers);
    if(!JS_DefineProperty(
        cx, obj,
        "_headers",
        tmp,
        NULL, NULL,
        JSPROP_READONLY
    )) {
        JS_ReportError(cx, "INTERNAL: Failed to set response headers.");
        goto done;
    }
    
    if(state.recvbuf) {
        state.recvbuf[state.read] = '\0';
        jsbody = dec_string(cx, state.recvbuf, state.read+1);
        if(!jsbody) {
            // If we can't decode the body as UTF-8 we forcefully
            // convert it to a string by just forcing each byte
            // to a jschar.
            jsbody = str_from_binary(cx, state.recvbuf, state.read);
            if(!jsbody) {
                if(!JS_IsExceptionPending(cx)) {
                    JS_ReportError(cx, "INTERNAL: Failed to decode body.");
                }
                goto done;
            }
        }
        tmp = STRING_TO_JSVAL(jsbody);
    } else {
        tmp = JS_GetEmptyStringValue(cx);
    }
    
    if(!JS_DefineProperty(
        cx, obj,
        "responseText",
        tmp,
        NULL, NULL,
        JSPROP_READONLY
    )) {
        JS_ReportError(cx, "INTERNAL: Failed to set responseText.");
        goto done;
    }
    
    ret = JS_TRUE;

done:
    if(state.recvbuf) JS_free(cx, state.recvbuf);
    return ret;
}