diff --git a/src/sbbs3/js_msgbase.c b/src/sbbs3/js_msgbase.c
index 3c09c3e512464955e2202d1d2e5a3cc457e9dd86..3acc4e17bcb3330f4787c78b3b7509742b2dd3a3 100644
--- a/src/sbbs3/js_msgbase.c
+++ b/src/sbbs3/js_msgbase.c
@@ -1550,6 +1550,7 @@ js_get_all_msg_headers(JSContext *cx, uintN argc, jsval *arglist)
     JSObject*	retobj;
 	char		numstr[16];
 	JSBool		include_votes=JS_FALSE;
+	post_t*		post;
 
 	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
 
@@ -1561,6 +1562,12 @@ js_get_all_msg_headers(JSContext *cx, uintN argc, jsval *arglist)
 	if(!SMB_IS_OPEN(&(priv->smb)))
 		return JS_TRUE;
 
+	if((post = malloc(priv->smb.status.total_msgs * sizeof(post_t))) == NULL) {
+		JS_ReportError(cx, "malloc error", WHERE);
+		return JS_FALSE;
+	}
+	memset(post, 0, priv->smb.status.total_msgs * sizeof(post_t));
+
 	if(argc && JSVAL_IS_BOOLEAN(argv[0]))
 		include_votes = JSVAL_TO_BOOLEAN(argv[0]);
 
@@ -1570,6 +1577,7 @@ js_get_all_msg_headers(JSContext *cx, uintN argc, jsval *arglist)
 	rc=JS_SUSPENDREQUEST(cx);
 	if((priv->status=smb_locksmbhdr(&(priv->smb)))!=SMB_SUCCESS) {
 		JS_RESUMEREQUEST(cx, rc);
+		free(post);
 		return JS_TRUE;
 	}
 	JS_RESUMEREQUEST(cx, rc);
@@ -1585,9 +1593,46 @@ js_get_all_msg_headers(JSContext *cx, uintN argc, jsval *arglist)
 		proto=NULL;
 
 	for(off=0; off < priv->smb.status.total_msgs; off++) {
+		smbmsg_t msg;
+
+		ZERO_VAR(msg);
+		msg.offset = off;
+
+		rc=JS_SUSPENDREQUEST(cx);
+		priv->status = smb_getmsgidx(&(priv->smb), &msg);
+		JS_RESUMEREQUEST(cx, rc);
+		if(priv->status != SMB_SUCCESS) {
+			smb_unlocksmbhdr(&(priv->smb)); 
+			free(post);
+			return JS_TRUE;
+		}
+		post[off].idx = msg.idx;
+		if(msg.idx.attr&MSG_VOTE) {
+			ulong u;
+			for(u = 0; u < off; u++)
+				if(post[u].idx.number == msg.idx.remsg)
+					break;
+			if(u < off) {
+				switch(msg.idx.attr&MSG_VOTE) {
+				case MSG_UPVOTE:
+					post[u].upvotes++;
+					break;
+				case MSG_DOWNVOTE:
+					post[u].downvotes++;
+					break;
+				}
+			}
+		}
+	}
+
+	for(off=0; off < priv->smb.status.total_msgs; off++) {
+		if((!include_votes) && (post[off].idx.attr&MSG_VOTE))
+			continue;
+
 		if((p=(privatemsg_t*)malloc(sizeof(privatemsg_t)))==NULL) {
 			smb_unlocksmbhdr(&(priv->smb)); 
 			JS_ReportError(cx,"malloc failed");
+			free(post);
 			return JS_FALSE;
 		}
 
@@ -1597,38 +1642,36 @@ js_get_all_msg_headers(JSContext *cx, uintN argc, jsval *arglist)
 		p->p=priv;
 		p->expand_fields=JS_TRUE;	/* This parameter defaults to true */
 
-		p->msg.offset=off;
+		p->msg.idx = post[off].idx;
+
 		rc=JS_SUSPENDREQUEST(cx);
-		if((priv->status=smb_getmsgidx(&(priv->smb), &(p->msg)))!=SMB_SUCCESS) {
+		priv->status = smb_getmsghdr(&(priv->smb), &(p->msg));
+		JS_RESUMEREQUEST(cx, rc);
+		if(priv->status != SMB_SUCCESS) {
 			smb_unlocksmbhdr(&(priv->smb)); 
-			JS_RESUMEREQUEST(cx, rc);
-			return JS_TRUE;
-		}
-
-		if(!include_votes && (p->msg.idx.attr&MSG_VOTE)) {
+			free(post);
 			free(p);
-			JS_RESUMEREQUEST(cx, rc);
-			continue;
-		}
-
-		if((priv->status=smb_getmsghdr(&(priv->smb), &(p->msg)))!=SMB_SUCCESS) {
-			smb_unlocksmbhdr(&(priv->smb)); 
-			JS_RESUMEREQUEST(cx, rc);
 			return JS_TRUE;
 		}
 
-		JS_RESUMEREQUEST(cx, rc);
-
 		if((hdrobj=JS_NewObject(cx,&js_msghdr_class,proto,obj))==NULL) {
 			smb_freemsgmem(&(p->msg));
 			smb_unlocksmbhdr(&(priv->smb)); 
+			free(post);
+			free(p);
 			return JS_TRUE;
 		}
 
+		JS_DefineProperty(cx, hdrobj, "upvotes", UINT_TO_JSVAL(post[off].upvotes)
+			,NULL,NULL,JSPROP_ENUMERATE);
+		JS_DefineProperty(cx, hdrobj, "downvotes", UINT_TO_JSVAL(post[off].downvotes)
+			,NULL,NULL,JSPROP_ENUMERATE);
+
 		if(!JS_SetPrivate(cx, hdrobj, p)) {
 			JS_ReportError(cx,"JS_SetPrivate failed");
-			free(p);
 			smb_unlocksmbhdr(&(priv->smb));
+			free(post);
+			free(p);
 			return JS_FALSE;
 		}
 
@@ -1637,6 +1680,7 @@ js_get_all_msg_headers(JSContext *cx, uintN argc, jsval *arglist)
 		JS_SetProperty(cx, retobj, numstr, &val);
 	}
 	smb_unlocksmbhdr(&(priv->smb));
+	free(post);
 
 	return JS_TRUE;
 }
@@ -2516,7 +2560,9 @@ static jsSyncMethodSpec js_msgbase_functions[] = {
 	,312
 	},
 	{"get_all_msg_headers", js_get_all_msg_headers, 1, JSTYPE_ARRAY, JSDOCSTR("[include_votes=<tt>false</tt>]")
-	,JSDOCSTR("returns an object of all message headers indexed by message number. Vote messages are exluded by default.")
+	,JSDOCSTR("returns an object of all message headers indexed by message number.<br>"
+	"Message headers returned by this function include 2 additional properties: <tt>upvotes</tt> and <tt>downvotes</tt>.<br>"
+	"Vote messages are exluded by default.")
 	,316
 	},
 	{"put_msg_header",	js_put_msg_header,	2, JSTYPE_BOOLEAN,	JSDOCSTR("[by_offset=<tt>false</tt>,] number, object header")