diff --git a/src/sbbs3/js_file.c b/src/sbbs3/js_file.c
index 0c32bc68677d66d3ebdc11c06193485fe55098bb..b750189449a2ff486cdc8ebc94f1850fd27e3782 100644
--- a/src/sbbs3/js_file.c
+++ b/src/sbbs3/js_file.c
@@ -41,6 +41,7 @@
 #include "uucode.h"
 #include "yenc.h"
 #include "ini_file.h"
+#include <stdbool.h>
 
 #if !defined(__unix__)
 	#include <conio.h>		/* for kbhit() */
@@ -1196,6 +1197,7 @@ js_iniGetObject(JSContext *cx, uintN argc, jsval *arglist)
 	private_t*	p;
 	named_string_t** list;
 	jsrefcount	rc;
+	bool		lowercase = false;
 
 	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
 
@@ -1206,9 +1208,15 @@ js_iniGetObject(JSContext *cx, uintN argc, jsval *arglist)
 	if(p->fp==NULL)
 		return(JS_TRUE);
 
-	if(argc>0 && argv[0]!=JSVAL_VOID && argv[0]!=JSVAL_NULL) {
-		JSVALUE_TO_MSTRING(cx, argv[0], section, NULL);
+	uintN argn = 0;
+	if(argc > argn && !JSVAL_IS_BOOLEAN(argv[argn])) {
+		JSVALUE_TO_MSTRING(cx, argv[argn], section, NULL);
 		HANDLE_PENDING(cx, section);
+		argn++;
+	}
+	if(argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) {
+		lowercase = JSVAL_TO_BOOLEAN(argv[argn]);
+		argn++;
 	}
 
 	rc=JS_SUSPENDREQUEST(cx);
@@ -1216,14 +1224,14 @@ js_iniGetObject(JSContext *cx, uintN argc, jsval *arglist)
 	FREE_AND_NULL(section);
 	JS_RESUMEREQUEST(cx, rc);
 
-	if(list==NULL) {	/* New behavior at request of MCMLXXIX: return NULL if specified section doesn't exist */
-		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
+	if(list==NULL)
 		return(JS_TRUE);
-	}
 
     object = JS_NewObject(cx, NULL, NULL, obj);
 
     for(i=0;list && list[i];i++) {
+		if(lowercase)
+			strlwr(list[i]->name);
 		JS_DefineProperty(cx, object, list[i]->name
 			,get_value(cx,list[i]->value)
 			,NULL,NULL,JSPROP_ENUMERATE);
@@ -1336,6 +1344,7 @@ js_iniGetAllObjects(JSContext *cx, uintN argc, jsval *arglist)
 	private_t*	p;
 	named_string_t** key_list;
 	jsrefcount	rc;
+	bool		lowercase = false;
 
 	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
 
@@ -1346,16 +1355,25 @@ js_iniGetAllObjects(JSContext *cx, uintN argc, jsval *arglist)
 	if(p->fp==NULL)
 		return(JS_TRUE);
 
-	if(argc)
-		JSVALUE_TO_MSTRING(cx, argv[0], name, NULL);
-	HANDLE_PENDING(cx, name);
-	if(name == NULL) {
-		JS_ReportError(cx, "Invalid NULL name property");
-		return JS_FALSE;
+	uintN argn = 0;
+	if(argc > argn && JSVAL_IS_STRING(argv[argn])) {
+		JSVALUE_TO_MSTRING(cx, argv[argn], name, NULL);
+		HANDLE_PENDING(cx, name);
+		if(name == NULL) {
+			JS_ReportError(cx, "Invalid name argument");
+			return JS_FALSE;
+		}
+		argn++;
+	}
+	if(argc > argn && JSVAL_IS_STRING(argv[argn])) {
+		JSVALUE_TO_MSTRING(cx, argv[argn], prefix, NULL);
+		argn++;
+	}
+	if(argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) {
+		lowercase = JSVAL_TO_BOOLEAN(argv[argn]);
+		argn++;
 	}
 
-	if(argc>1)
-		JSVALUE_TO_MSTRING(cx, argv[1], prefix, NULL);
 	if(JS_IsExceptionPending(cx)) {
 		FREE_AND_NULL(prefix);
 		if(name != name_def)
@@ -1370,10 +1388,11 @@ js_iniGetAllObjects(JSContext *cx, uintN argc, jsval *arglist)
 	JS_RESUMEREQUEST(cx, rc);
     for(i=0;sec_list && sec_list[i];i++) {
 	    object = JS_NewObject(cx, NULL, NULL, obj);
-
 		sec_name=sec_list[i];
 		if(prefix!=NULL)
 			sec_name+=strlen(prefix);
+		if(lowercase)
+			strlwr(sec_name);
 		JS_DefineProperty(cx, object, name
 			,STRING_TO_JSVAL(JS_NewStringCopyZ(cx,sec_name))
 			,NULL,NULL,JSPROP_ENUMERATE);
@@ -1381,10 +1400,13 @@ js_iniGetAllObjects(JSContext *cx, uintN argc, jsval *arglist)
 		rc=JS_SUSPENDREQUEST(cx);
 		key_list = iniReadNamedStringList(p->fp,sec_list[i]);
 		JS_RESUMEREQUEST(cx, rc);
-		for(k=0;key_list && key_list[k];k++)
+		for(k=0;key_list && key_list[k];k++) {
+			if(lowercase)
+				strlwr(key_list[k]->name);
 			JS_DefineProperty(cx, object, key_list[k]->name
 				,get_value(cx,key_list[k]->value)
 				,NULL,NULL,JSPROP_ENUMERATE);
+		}
 		rc=JS_SUSPENDREQUEST(cx);
 		iniFreeNamedStringList(key_list);
 		JS_RESUMEREQUEST(cx, rc);
@@ -2853,9 +2875,9 @@ static jsSyncMethodSpec js_file_functions[] = {
 		"to set a key in the <i>root</i> section, pass <i>null</i> for <i>section</i>. ")
 	,312
 	},
-	{"iniGetObject",	js_iniGetObject,	1,	JSTYPE_OBJECT,	JSDOCSTR("[section=<i>root</i>]")
+	{"iniGetObject",	js_iniGetObject,	1,	JSTYPE_OBJECT,	JSDOCSTR("[section=<i>root</i>] [lowercase=<tt>false</tt>]")
 	,JSDOCSTR("parse an entire section from a .ini file "
-		"and return all of its keys and values as properties of an object. "
+		"and return all of its keys (optionally lowercased) and values as properties of an object. "
 		"if <i>section</i> is undefined, returns keys and values from the <i>root</i> section. "
 		"Returns <i>null</i> if the specified <i>section</i> does not exist in the file or the file has not been opened.")
 	,311
@@ -2868,11 +2890,11 @@ static jsSyncMethodSpec js_file_functions[] = {
 		"If your intention is to <i>replace</i> an existing section, use the <tt>iniRemoveSection</tt> function first." )
 	,312
 	},
-	{"iniGetAllObjects",js_iniGetAllObjects,1,	JSTYPE_ARRAY,	JSDOCSTR("[name_property] [,prefix=<i>none</i>]")
+	{"iniGetAllObjects",js_iniGetAllObjects,1,	JSTYPE_ARRAY,	JSDOCSTR("[name_property] [,prefix=<i>none</i>] [lowercase=<tt>false</tt>]")
 	,JSDOCSTR("parse all sections from a .ini file and return all (non-<i>root</i>) sections "
-		"in an array of objects with each section's keys as properties of each object. "
+		"in an array of objects with each section's keys (optionally lowercased) as properties of each object. "
 		"<i>name_property</i> is the name of the property to create to contain the section's name "
-		"(default is <tt>\"name\"</tt>), "
+		"(optionally lowercased, default is <tt>\"name\"</tt>), "
 		"the optional <i>prefix</i> has the same use as in the <tt>iniGetSections</tt> method, "
 		"if a <i>prefix</i> is specified, it is removed from each section's name" )
 	,311