diff --git a/src/sbbs3/js_user.c b/src/sbbs3/js_user.c
index 51cb9c159aa649c8a1b9ba913dc6c0c9b362ed45..59f2f2a70d750490226fcd3bc31caf02000296b5 100644
--- a/src/sbbs3/js_user.c
+++ b/src/sbbs3/js_user.c
@@ -81,6 +81,7 @@ enum {
 	,USER_PROP_ULS       
 	,USER_PROP_DLB       
 	,USER_PROP_DLS       
+	,USER_PROP_DLCPS
 	,USER_PROP_CDT		
 	,USER_PROP_MIN		
 	,USER_PROP_LEVEL 	
@@ -279,6 +280,9 @@ static JSBool js_user_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 		case USER_PROP_DLS:
 			val=p->user->dls;
 			break;
+		case USER_PROP_DLCPS:
+			val=p->user->dlcps;
+			break;
 		case USER_PROP_CDT:
 			*vp = DOUBLE_TO_JSVAL((jsdouble)p->user->cdt);
 			JS_RESUMEREQUEST(cx, rc);
@@ -961,6 +965,7 @@ static jsSyncPropertySpec js_user_stats_properties[] = {
 	{	"files_uploaded"	,USER_PROP_ULS        	,USER_PROP_FLAGS,		310 },
 	{	"bytes_downloaded"	,USER_PROP_DLB        	,USER_PROP_FLAGS,		310 },
 	{	"files_downloaded"	,USER_PROP_DLS        	,USER_PROP_FLAGS,		310 },
+	{	"download_cps"		,USER_PROP_DLCPS       	,USER_PROP_FLAGS,		320 },
 	{	"leech_attempts"	,USER_PROP_LEECH 	 	,USER_PROP_FLAGS,		310 },
 	{	"mail_waiting"		,USER_PROP_MAIL_WAITING	,USER_PROP_FLAGS,		312	},
 	{	"read_mail_waiting"	,USER_PROP_READ_WAITING	,USER_PROP_FLAGS,		31802 },
@@ -989,6 +994,7 @@ static char* user_stats_prop_desc[] = {
 	,"total files uploaded"
 	,"total bytes downloaded"
 	,"total files downloaded"
+	,"latest average download rate, in characters (bytes) per second"
 	,"suspected leech downloads"
 	,"total number of e-mail messages currently waiting in inbox"
 	,"number of read e-mail messages currently waiting in inbox"
diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp
index 11e513bf266cd14517c97658cfd98ed4ecabc7be..066e6954b1f14f578c344aa59db85662b404c7ab 100644
--- a/src/sbbs3/logon.cpp
+++ b/src/sbbs3/logon.cpp
@@ -54,6 +54,9 @@ bool sbbs_t::logon()
 	js_create_user_objects(js_cx, js_glob);
 #endif
 
+	if(useron.dlcps)
+		cur_cps = useron.dlcps;
+
 	if(useron.rest&FLAG('Q'))
 		sys_status ^= SS_QWKLOGON;
 
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index a48521b085575eb8ad24a9f87ec8f140f3482148..184993943df69a99c6c26be94024b50f83a9b5a9 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -4024,7 +4024,6 @@ void sbbs_t::reset_logon_vars(void)
 		curdir[i]=0;
 	for(i=0;i<cfg.total_grps;i++)
 		cursub[i]=0;
-	cur_cps=3000;
     cur_rate=30000;
     dte_rate=38400;
 	cur_output_rate = output_rate_unlimited;
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index 3aac8e04b87e55e607d8aaac63ec8bc973aa5fd2..da4368922d2761eabdc8ee0dec7681e7ce24ed2a 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -543,7 +543,7 @@ public:
 	ushort	node_connection = NODE_CONNECTION_TELNET;
 	char	connection[LEN_MODEM+1];	/* Connection Description */
 	uint	cur_rate=0;		/* Current Connection (DCE) Rate */
-	uint	cur_cps=0;		/* Current Average Transfer CPS */
+	uint	cur_cps=10000;	/* Current Average Download CPS */
 	uint	dte_rate=0;		/* Current COM Port (DTE) Rate */
 	time_t 	timeout=0;		/* User inactivity timeout reference */
 	uint 	timeleft_warn=0;/* low timeleft warning flag */
diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h
index 7662c74f0f8cf84f51596bdc1666b693761b313b..9f2b2dfd06d4dcaad3763514e2a60273fa1b66e4 100644
--- a/src/sbbs3/sbbsdefs.h
+++ b/src/sbbs3/sbbsdefs.h
@@ -960,6 +960,7 @@ typedef struct {						/* Users information */
 	uint64_t freecdt;					/* Free credits (renewed daily) */
 	uint64_t ulb;						/* Total bytes uploaded */
 	uint64_t dlb;						/* Total bytes downloaded */
+	uint32_t dlcps;						/* Last download rate (in CPS) */
 	time32_t firston,					/* Date/Time first called */
 			laston, 					/* Last logoff date/time */
 			expire, 					/* Expiration date */
diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index 343f7a84895278ffb63ff66179e88d58859fee5c..cc78be39245b57e1278af4f87c40724a3800bfc3 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -450,6 +450,7 @@ int parseuserdat(scfg_t* cfg, char *userdat, user_t *user, char* field[])
 	user->uls = (ushort)strtoul(field[USER_ULS], NULL, 0);
 	user->dlb = strtoull(field[USER_DLB], NULL, 0);
 	user->dls = (ushort)strtoul(field[USER_DLS], NULL, 0);
+	user->dlcps = (uint32_t)strtoul(field[USER_DLCPS], NULL, 0);
 	user->leech = (uchar)strtoul(field[USER_LEECH], NULL, 0);
 
 	SAFECOPY(user->pass, field[USER_PASS]);
@@ -662,7 +663,7 @@ BOOL format_userdat(scfg_t* cfg, user_t* user, char userdat[])
 		"%u\t"			// USER_ULS
 		"%" PRIu64 "\t" // USER_DLB
 		"%u\t"			// USER_DLS
-		"%u\t"	// USER_LEECH
+		"%" PRIu32 "\t" // USER_DLCPS
 		"%s\t"	// USER_PASS
 		"%s\t"	// USER_PWMOD
 		"%u\t"	// USER_LEVEL
@@ -677,6 +678,7 @@ BOOL format_userdat(scfg_t* cfg, user_t* user, char userdat[])
 		"%" PRIu32 "\t" // USER_MIN
 		"%u\t"	// USER_TEXTRA
 		"%s\t"	// USER_EXPIRE
+		"%u\t"	// USER_LEECH
 		,user->number
 		,user->alias
 		,user->name
@@ -723,7 +725,7 @@ BOOL format_userdat(scfg_t* cfg, user_t* user, char userdat[])
 		,(uint)user->uls
 		,user->dlb
 		,(uint)user->dls
-		,(uint)user->leech
+		,user->dlcps
 		,user->pass
 		,pwmod
 		,(uint)user->level
@@ -738,6 +740,7 @@ BOOL format_userdat(scfg_t* cfg, user_t* user, char userdat[])
 		,user->min
 		,user->textra
 		,expire
+		,(uint)user->leech
 	);
 	if(len > USER_RECORD_LEN || len < 0) // truncated?
 		return FALSE;
@@ -3116,6 +3119,7 @@ size_t user_field_len(enum user_field fnum)
 		case USER_ULS:			return sizeof(user.uls);
 		case USER_DLB:			return sizeof(user.dlb);
 		case USER_DLS:			return sizeof(user.dls);
+		case USER_DLCPS:		return sizeof(user.dlcps);
 		case USER_LEECH:		return sizeof(user.leech);
 
 		// Security:
diff --git a/src/sbbs3/userfields.h b/src/sbbs3/userfields.h
index 67938011a8cf07bfa9a1e17c4525ef9055128fc9..9283da047ad6f025148fe67ba61b85d6aaa0406e 100644
--- a/src/sbbs3/userfields.h
+++ b/src/sbbs3/userfields.h
@@ -81,7 +81,7 @@ enum user_field {
 	USER_ULS,
 	USER_DLB,
 	USER_DLS,
-	USER_LEECH,
+	USER_DLCPS,
 
 	// Security:
 	USER_PASS,
@@ -98,6 +98,7 @@ enum user_field {
 	USER_MIN,
 	USER_TEXTRA,
 	USER_EXPIRE,
+	USER_LEECH,
 
 	// Last:
 	USER_FIELD_COUNT