Skip to content
Snippets Groups Projects
js_msgbase.c 112 KiB
Newer Older
/* Synchronet JavaScript "MsgBase" Object */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
	smb_t smb;
	int smb_result;
	JSBool debug;
	private_t *p;
	bool expand_fields;
	bool enumerated;
	smbmsg_t msg;
	post_t post;
/* Destructor */

static void js_finalize_msgbase(JSContext *cx, JSObject *obj)
{
	private_t* p;
	if ((p = (private_t*)JS_GetPrivate(cx, obj)) == NULL)
	if (SMB_IS_OPEN(&(p->smb)))
		smb_close(&(p->smb));

	free(p);

	JS_SetPrivate(cx, obj, NULL);
}

/* Methods */

js_open(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsrefcount rc;
	scfg_t*    scfg;

	scfg = JS_GetRuntimePrivate(JS_GetRuntime(cx));
	if (scfg == NULL) {
		JS_ReportError(cx, "JS_GetRuntimePrivate returned NULL");
		return JS_FALSE;
	}
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_msgbase_class)) == NULL) {
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if (!subnum_is_valid(scfg, p->smb.subnum)
	    && strchr(p->smb.file, '/') == NULL
	    && strchr(p->smb.file, '\\') == NULL) {
		JS_ReportError(cx, "Unrecognized msgbase code: %s", p->smb.file);
	rc = JS_SUSPENDREQUEST(cx);
	if (!subnum_is_valid(scfg, p->smb.subnum) && p->smb.subnum != INVALID_SUB)
		p->smb_result = smb_open(&(p->smb));
	else
		p->smb_result = smb_open_sub(scfg, &(p->smb), p->smb.subnum);
	if (p->smb_result != SMB_SUCCESS) {
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
js_close(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_msgbase_class)) == NULL) {
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	rc = JS_SUSPENDREQUEST(cx);
static bool parse_recipient_object(JSContext* cx, private_t* p, JSObject* hdr, smbmsg_t* msg)
	char*   cp = NULL;
	size_t  cp_sz = 0;
	char    to[256];
	ushort  nettype = NET_UNKNOWN;
	ushort  agent;
	int32   i32;
	jsval   val;
	scfg_t* scfg;
	int     smb_result = SMB_SUCCESS;
	scfg = JS_GetRuntimePrivate(JS_GetRuntime(cx));
	if (JS_GetProperty(cx, hdr, "to", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"to\" string in recipient object");
		if ((p != NULL) && (p->smb.status.attr & SMB_EMAIL)) { /* e-mail */
			JS_ReportError(cx, "\"to\" property not included in email recipient object");
			return false;                  /* "to" property required */
		cp = strdup("All");
	if ((smb_result = smb_hfield_str(msg, RECIPIENT, cp)) != SMB_SUCCESS) {
		JS_ReportError(cx, "Error %d adding RECIPIENT field to message header", smb_result);
	if ((p != NULL) && !(p->smb.status.attr & SMB_EMAIL)) {
		SAFECOPY(to, cp);
		msg->idx.to = crc16(to, 0);
	if (JS_GetProperty(cx, hdr, "to_list", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"to_list\" string in recipient object");
		if ((smb_result = smb_hfield_str(msg, RECIPIENTLIST, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding RECIPIENTLIST field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "cc_list", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"cc_list\" string in recipient object");
		if ((smb_result = smb_hfield_str(msg, SMB_CARBONCOPY, cp)) != SMB_SUCCESS) {
			free(cp);
			JS_ReportError(cx, "Error %d adding SMB_CARBONCOPY field to message header", smb_result);
			goto err;
		}
	}

	if (JS_GetProperty(cx, hdr, "to_ext", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"to_ext\" string in recipient object");
		if ((smb_result = smb_hfield_str(msg, RECIPIENTEXT, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding RECIPIENTEXT field to message header", smb_result);
			goto err;
		if ((p != NULL) && (p->smb.status.attr & SMB_EMAIL))
			msg->idx.to = atoi(cp);
	if (JS_GetProperty(cx, hdr, "to_org", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"to_org\" string in recipient object");
		if ((smb_result = smb_hfield_str(msg, RECIPIENTORG, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding RECIPIENTORG field to message header", smb_result);
			goto err;
	if (JS_GetProperty(cx, hdr, "to_net_type", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToInt32(cx, val, &i32)) {
		nettype = (ushort)i32;
	if (JS_GetProperty(cx, hdr, "to_net_addr", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"to_net_addr\" string in recipient object");
		if ((smb_result = smb_hfield_netaddr(msg, RECIPIENTNETADDR, cp, &nettype)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding RECIPIENTADDR field to message header", smb_result);
			goto err;
	if (nettype != NET_UNKNOWN) {
		if ((p != NULL) && (p->smb.status.attr & SMB_EMAIL)) {
			if (nettype == NET_QWK && msg->idx.to == 0) {
				msg->idx.to = qwk_route(scfg, msg->to_net.addr, fulladdr, sizeof(fulladdr) - 1);
				if (fulladdr[0] == 0) {
					JS_ReportError(cx, "Unroutable QWKnet \"to_net_addr\" (%s) in recipient object"
					               , msg->to_net.addr);
				if ((smb_result = smb_hfield_str(msg, RECIPIENTNETADDR, fulladdr)) != SMB_SUCCESS) {
					JS_ReportError(cx, "Error %d adding RECIPIENTADDR field to message header"
				if (msg->idx.to != 0) {
					sprintf(ext, "%u", msg->idx.to);
					if ((smb_result = smb_hfield_str(msg, RECIPIENTEXT, ext)) != SMB_SUCCESS) {
						JS_ReportError(cx, "Error %d adding RECIPIENTEXT field to message header"
		if ((smb_result = smb_hfield_bin(msg, RECIPIENTNETTYPE, nettype)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding RECIPIENTNETTYPE field to message header", smb_result);
			goto err;
	if (JS_GetProperty(cx, hdr, "to_agent", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToInt32(cx, val, &i32))
		agent = (ushort)i32;
		if ((smb_result = smb_hfield_bin(msg, RECIPIENTAGENT, agent)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding RECIPIENTAGENT field to message header", smb_result);
			goto err;
rswindell's avatar
rswindell committed
	}
	if (smb_result != SMB_SUCCESS && p != NULL)
static bool parse_header_object(JSContext* cx, private_t* p, JSObject* hdr, smbmsg_t* msg
	char*     cp = NULL;
	size_t    cp_sz = 0;
	char      from[256];
	ushort    nettype = NET_UNKNOWN;
	ushort    type;
	ushort    agent;
	int32     i32;
	uint32    u32;
	jsval     val;
	JSObject* array;
	JSObject* field;
	jsuint    i, len;
	int       smb_result = SMB_SUCCESS;

	if (hdr == NULL) {
		JS_ReportError(cx, "NULL header object");
	if (recipient && !parse_recipient_object(cx, p, hdr, msg))
	if (msg->hdr.type != SMB_MSG_TYPE_BALLOT) {
		if (JS_GetProperty(cx, hdr, "subject", &val) && !JSVAL_NULL_OR_VOID(val)) {
			JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
			HANDLE_PENDING(cx, cp);
				JS_ReportError(cx, "Invalid \"subject\" string in header object");
				goto err;
			}
		} else
		if ((smb_result = smb_hfield_str(msg, SUBJECT, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SUBJECT field to message header", smb_result);
		msg->idx.subj = smb_subject_crc(cp);
	if (JS_GetProperty(cx, hdr, "from", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"from\" string in header object");
		}
	} else {
		JS_ReportError(cx, "\"from\" property required in header");
		goto err;   /* "from" property required */
	if ((smb_result = smb_hfield_str(msg, SENDER, cp)) != SMB_SUCCESS) {
		JS_ReportError(cx, "Error %d adding SENDER field to message header", smb_result);
	if ((p != NULL) && !(p->smb.status.attr & SMB_EMAIL) && msg->hdr.type != SMB_MSG_TYPE_BALLOT) {
		SAFECOPY(from, cp);
		msg->idx.from = crc16(from, 0);

	/* Optional Header Fields */
	if (JS_GetProperty(cx, hdr, "from_ext", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"from_ext\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SENDEREXT, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDEREXT field to message header", smb_result);
		if ((p != NULL) && (p->smb.status.attr & SMB_EMAIL))
			msg->idx.from = atoi(cp);
	if (JS_GetProperty(cx, hdr, "from_org", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"from_org\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SENDERORG, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERORG field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "from_net_type", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToInt32(cx, val, &i32)) {
		nettype = (ushort)i32;
	if (JS_GetProperty(cx, hdr, "from_net_addr", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"from_net_addr\" string in header object");
		if ((smb_result = smb_hfield_netaddr(msg, SENDERNETADDR, cp, &nettype)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERNETADDR field to message header", smb_result);
	if (nettype != NET_UNKNOWN) {
		if ((p != NULL) && (p->smb.status.attr & SMB_EMAIL))
			msg->idx.from = 0;
		if ((smb_result = smb_hfield_bin(msg, SENDERNETTYPE, nettype)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERNETTYPE field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "from_agent", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToInt32(cx, val, &i32))
		agent = (ushort)i32;
		if ((smb_result = smb_hfield_bin(msg, SENDERAGENT, agent)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERAGENT field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "from_ip_addr", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"from_ip_addr\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SENDERIPADDR, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERIPADDR field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "from_host_name", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"from_host_name\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SENDERHOSTNAME, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERHOSTNAME field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "from_protocol", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"from_protocol\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SENDERPROTOCOL, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERPROTOCOL field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "from_port", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"from_port\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SENDERPORT, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERPORT field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "sender_userid", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"sender_userid\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SENDERUSERID, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERUSERID field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "sender_server", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"sender_server\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SENDERSERVER, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERSERVER field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "sender_time", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"sender_time\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SENDERTIME, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SENDERTIME field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "replyto", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"replyto\" string in header object");
		if ((smb_result = smb_hfield_str(msg, REPLYTO, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding REPLYTO field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "replyto_ext", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"replyto_ext\" string in header object");
		if ((smb_result = smb_hfield_str(msg, REPLYTOEXT, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding REPLYTOEXT field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "replyto_org", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"replyto_org\" string in header object");
		if ((smb_result = smb_hfield_str(msg, REPLYTOORG, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding REPLYTOORG field to message header", smb_result);
	nettype = NET_UNKNOWN;
	if (JS_GetProperty(cx, hdr, "replyto_net_type", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToInt32(cx, val, &i32))
		nettype = (ushort)i32;
	if (JS_GetProperty(cx, hdr, "replyto_net_addr", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"replyto_net_addr\" string in header object");
		if ((smb_result = smb_hfield_netaddr(msg, REPLYTONETADDR, cp, &nettype)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding REPLYTONETADDR field to message header", smb_result);
	if (nettype != NET_UNKNOWN) {
		if ((smb_result = smb_hfield_bin(msg, REPLYTONETTYPE, nettype)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding REPLYTONETTYPE field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "replyto_agent", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToInt32(cx, val, &i32))
		agent = (ushort)i32;
		if ((smb_result = smb_hfield_bin(msg, REPLYTOAGENT, agent)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding REPLYTOAGENT field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "replyto_list", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"replyto_list\" string in header object");
			goto err;
		}
		if ((smb_result = smb_hfield_str(msg, REPLYTOLIST, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding REPLYTOLIST field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "id", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"id\" string in header object");
		if ((smb_result = smb_hfield_str(msg, RFC822MSGID, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding RFC822MSGID field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "reply_id", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"reply_id\" string in header object");
		if ((smb_result = smb_hfield_str(msg, RFC822REPLYID, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding RFC822REPLYID field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "reverse_path", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"reverse_path\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SMTPREVERSEPATH, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SMTPREVERSEPATH field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "forward_path", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"forward_path\" string in header object");
		if ((smb_result = smb_hfield_str(msg, SMTPFORWARDPATH, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding SMTPFORWARDPATH field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "path", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"path\" string in header object");
		if ((smb_result = smb_hfield_str(msg, USENETPATH, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding USENETPATH field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "newsgroups", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"newsgroups\" string in header object");
		if ((smb_result = smb_hfield_str(msg, USENETNEWSGROUPS, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding USENETNEWSGROUPS field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "ftn_msgid", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"ftn_msgid\" string in header object");
		if ((smb_result = smb_hfield_str(msg, FIDOMSGID, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding FIDOMSGID field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "ftn_reply", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"ftn_reply\" string in header object");
		if ((smb_result = smb_hfield_str(msg, FIDOREPLYID, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding FIDOREPLYID field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "ftn_area", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"ftn_area\" string in header object");
		if ((smb_result = smb_hfield_str(msg, FIDOAREA, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding FIDOAREA field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "ftn_flags", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"ftn_flags\" string in header object");
		if ((smb_result = smb_hfield_str(msg, FIDOFLAGS, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding FIDOFLAGS field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "ftn_pid", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"ftn_pid\" string in header object");
		if ((smb_result = smb_hfield_str(msg, FIDOPID, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding FIDOPID field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "ftn_tid", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"ftn_tid\" string in header object");
		if ((smb_result = smb_hfield_str(msg, FIDOTID, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding FIDOTID field to message header", smb_result);
	if (JS_GetProperty(cx, hdr, "ftn_charset", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"ftn_charset\" string in header object");
			goto err;
		}
		if ((smb_result = smb_hfield_str(msg, FIDOCHARSET, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding FIDOCHARSET field to message header", smb_result);
			goto err;
		}
	}

	if (JS_GetProperty(cx, hdr, "ftn_bbsid", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"ftn_bbsid\" string in header object");
			goto err;
		}
		if ((smb_result = smb_hfield_str(msg, FIDOBBSID, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding FIDOBBSID field to message header", smb_result);
			goto err;
		}
	}

	if (JS_GetProperty(cx, hdr, "date", &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"date\" string in header object");
		msg->hdr.when_written = rfc822date(cp);
	const char* prop_name = "summary";
	uint16_t    hfield_type = SMB_SUMMARY;
	if (JS_GetProperty(cx, hdr, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"%s\" string in header object", prop_name);
			goto err;
		}
		if ((smb_result = smb_hfield_str(msg, hfield_type, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding %s field to message header", smb_result, smb_hfieldtype(hfield_type));
			goto err;
		}
	}

	prop_name = "tags";
	hfield_type = SMB_TAGS;
	if (JS_GetProperty(cx, hdr, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"%s\" string in header object", prop_name);
			goto err;
		}
		if ((smb_result = smb_hfield_str(msg, hfield_type, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding %s field to message header", smb_result, smb_hfieldtype(hfield_type));
	prop_name = "editor";
	hfield_type = SMB_EDITOR;
	if (JS_GetProperty(cx, hdr, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
		HANDLE_PENDING(cx, cp);
			JS_ReportError(cx, "Invalid \"%s\" string in header object", prop_name);
			goto err;
		}
		if ((smb_result = smb_hfield_str(msg, hfield_type, cp)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding %s field to message header", smb_result, smb_hfieldtype(hfield_type));
			goto err;
		}
	}

	prop_name = "columns";
	hfield_type = SMB_COLUMNS;
	if (JS_GetProperty(cx, hdr, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32)) {
			JS_ReportError(cx, "Invalid \"%s\" number in header object", prop_name);
			goto err;
		}
		uint8_t u8 = u32;
		if ((smb_result = smb_hfield_bin(msg, hfield_type, u8)) != SMB_SUCCESS) {
			JS_ReportError(cx, "Error %d adding %s field to message header", smb_result, smb_hfieldtype(hfield_type));
	/* Numeric Header Fields */
	if (JS_GetProperty(cx, hdr, "attr", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.attr = (ushort)u32;
		msg->idx.attr = msg->hdr.attr;
	if (JS_GetProperty(cx, hdr, "votes", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.votes = (ushort)u32;
		if (msg->hdr.type == SMB_MSG_TYPE_BALLOT)
			msg->idx.votes = msg->hdr.votes;
	if (JS_GetProperty(cx, hdr, "auxattr", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.auxattr = u32;
	if (JS_GetProperty(cx, hdr, "netattr", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.netattr = u32;
	if (JS_GetProperty(cx, hdr, "when_written_time", &val) && !JSVAL_NULL_OR_VOID(val))  {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.when_written = smb_when(u32, msg->hdr.when_written.zone);
	if (JS_GetProperty(cx, hdr, "when_written_zone", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToInt32(cx, val, &i32))
		msg->hdr.when_written.zone = (short)i32;
	if (JS_GetProperty(cx, hdr, "when_imported_time", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.when_imported.time = u32;
	if (JS_GetProperty(cx, hdr, "when_imported_zone", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToInt32(cx, val, &i32))
		msg->hdr.when_imported.zone = (short)i32;
	if (JS_GetProperty(cx, hdr, "thread_id", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.thread_id = u32;
	if ((JS_GetProperty(cx, hdr, "thread_orig", &val) && (!JSVAL_NULL_OR_VOID(val)))
	    || (JS_GetProperty(cx, hdr, "thread_back", &val) && !JSVAL_NULL_OR_VOID(val))) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.thread_back = u32;
	if (JS_GetProperty(cx, hdr, "thread_next", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.thread_next = u32;
	if (JS_GetProperty(cx, hdr, "thread_first", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.thread_first = u32;
	if (JS_GetProperty(cx, hdr, "delivery_attempts", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.delivery_attempts = u32;
	if (JS_GetProperty(cx, hdr, "priority", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToInt32(cx, val, &i32))
		msg->hdr.priority = i32;
	if (JS_GetProperty(cx, hdr, "field_list", &val) && JSVAL_IS_OBJECT(val)) {
		array = JSVAL_TO_OBJECT(val);
		len = 0;
		if (array == NULL || !JS_GetArrayLength(cx, array, &len)) {
			JS_ReportError(cx, "Invalid \"field_list\" array in header object");
		for (i = 0; i < len; i++) {
			if (!JS_GetElement(cx, array, i, &val))
			if (!JSVAL_IS_OBJECT(val))
			field = JSVAL_TO_OBJECT(val);
			if (field == NULL || !JS_GetProperty(cx, field, "type", &val))
			if (JSVAL_IS_STRING(val)) {
				JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
				HANDLE_PENDING(cx, cp);
				type = smb_hfieldtypelookup(cp);
				if (!JS_ValueToInt32(cx, val, &i32))
				type = (ushort)i32;
			if (!JS_GetProperty(cx, field, "data", &val))
			JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
			HANDLE_PENDING(cx, cp);
				JS_ReportError(cx, "Invalid data string in \"field_list\" array");
			if ((smb_result = smb_hfield_str(msg, type, cp)) != SMB_SUCCESS) {
				JS_ReportError(cx, "Error %d adding field (type %02Xh) to message header", smb_result, type);
	if (msg->hdr.number == 0 && JS_GetProperty(cx, hdr, "number", &val) && !JSVAL_NULL_OR_VOID(val)) {
		if (!JS_ValueToECMAUint32(cx, val, &u32))
		msg->hdr.number = u32;
	if (smb_result != SMB_SUCCESS && p != NULL)