diff --git a/src/sbbs3/file.cpp b/src/sbbs3/file.cpp
index 532eb42e6be1bd713cb1b33c0a8a3f61b4e569cb..f66c768d6eec0ad314da849d48388b74a1a2ca9e 100644
--- a/src/sbbs3/file.cpp
+++ b/src/sbbs3/file.cpp
@@ -231,7 +231,7 @@ bool sbbs_t::movefile(smb_t* smb, file_t* f, int newdir)
 		return false; 
 	}
 
-	if(!addfile(&cfg, newdir, f, f->extdesc))
+	if(!addfile(&cfg, newdir, f, f->extdesc, /* client: */NULL))
 		return false;
 	removefile(smb, f);
 	bprintf(text[MovedFile],f->name
diff --git a/src/sbbs3/filedat.c b/src/sbbs3/filedat.c
index 2e58056de0d261526f4c7a91e2874c03b989e243..6fbc321c8f5aae945c0df33b00def90602dc4a75 100644
--- a/src/sbbs3/filedat.c
+++ b/src/sbbs3/filedat.c
@@ -585,7 +585,28 @@ bool hashfile(scfg_t* cfg, file_t* f)
 	return result;
 }
 
-bool addfile(scfg_t* cfg, uint dirnum, file_t* f, const char* extdesc)
+int file_client_hfields(file_t* f, client_t* client)
+{
+	int		i;
+
+	if(client == NULL)
+		return -1;
+
+	if(*client->addr && (i = smb_hfield_str(f, SENDERIPADDR, client->addr)) != SMB_SUCCESS)
+		return i;
+	if(*client->host && (i = smb_hfield_str(f, SENDERHOSTNAME, client->host)) != SMB_SUCCESS)
+		return i;
+	if(client->protocol != NULL && (i = smb_hfield_str(f, SENDERPROTOCOL, client->protocol)) != SMB_SUCCESS)
+		return i;
+	if(client->port) {
+		char	port[16];
+		SAFEPRINTF(port,"%u",client->port);
+		return smb_hfield_str(f, SENDERPORT, port);
+	}
+	return SMB_SUCCESS;
+}
+
+bool addfile(scfg_t* cfg, uint dirnum, file_t* f, const char* extdesc, client_t* client)
 {
 	char fpath[MAX_PATH + 1];
 	smb_t smb;
@@ -594,6 +615,7 @@ bool addfile(scfg_t* cfg, uint dirnum, file_t* f, const char* extdesc)
 		return false;
 
 	getfilepath(cfg, f, fpath);
+	file_client_hfields(f, client);
 	int result = smb_addfile(&smb, f, SMB_SELFPACK, extdesc, fpath);
 	smb_close(&smb);
 	return result == SMB_SUCCESS;
diff --git a/src/sbbs3/filedat.h b/src/sbbs3/filedat.h
index 04d24f13cc817833a93d1cf7c6964ed66ed6d3ab..537bc713765818d5032031d992b93f56b528e427 100644
--- a/src/sbbs3/filedat.h
+++ b/src/sbbs3/filedat.h
@@ -23,6 +23,7 @@
 #define _FILEDAT_H_
 
 #include "scfgdefs.h"	// scfg_t
+#include "client.h"
 #include "dllexport.h"
 #include "smblib.h"
 
@@ -52,13 +53,14 @@ DLLEXPORT time_t		getfiletime(scfg_t*, file_t*);
 DLLEXPORT ulong			gettimetodl(scfg_t*, file_t*, uint rate_cps);
 DLLEXPORT ulong			getuserxfers(scfg_t*, const char* from, uint to);
 DLLEXPORT bool			hashfile(scfg_t*, file_t*);
-DLLEXPORT bool			addfile(scfg_t*, uint dirnum, file_t*, const char* extdesc);
+DLLEXPORT bool			addfile(scfg_t*, uint dirnum, file_t*, const char* extdesc, client_t*);
 DLLEXPORT bool			removefile(scfg_t*, uint dirnum, const char* filename);
 DLLEXPORT char*			format_filename(const char* fname, char* buf, size_t, bool pad);
 DLLEXPORT bool			extract_diz(scfg_t*, file_t*, str_list_t diz_fname, char* path, size_t);
 DLLEXPORT str_list_t	read_diz(const char* path);
 DLLEXPORT char*			format_diz(str_list_t lines, char*, size_t maxlen, bool allow_ansi);
 DLLEXPORT char*			prep_file_desc(const char *src, char* dst);
+DLLEXPORT int			file_client_hfields(file_t*, client_t*);
 
 DLLEXPORT str_list_t	directory(const char* path);
 DLLEXPORT long			create_archive(const char* archive, const char* format
diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index 11d86b747db27c8c3360a1e9ac626ded49b3dead..8b4a9542b7a527f90bfeacbf0ee00b776ae063f9 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -1093,7 +1093,7 @@ static void receive_thread(void* arg)
 						,xfer.ctrl_sock, xfer.user->alias, f.name);
 				/* need to update the index here */
 			} else {
-				if(!addfile(&scfg, xfer.dir, &f, extdesc))
+				if(!addfile(&scfg, xfer.dir, &f, extdesc, xfer.client))
 					lprintf(LOG_ERR,"%04d <%s> !DATA ERROR adding file (%s) to database"
 						,xfer.ctrl_sock, xfer.user->alias, f.name);
 			}
diff --git a/src/sbbs3/js_filebase.c b/src/sbbs3/js_filebase.c
index f984d21c3c1182d2a1ac3e273ffbb2d108560818..31179178d9f35ee094fb500b76510de1f900816d 100644
--- a/src/sbbs3/js_filebase.c
+++ b/src/sbbs3/js_filebase.c
@@ -185,6 +185,26 @@ set_file_properties(JSContext *cx, JSObject* obj, file_t* f, enum file_detail de
 			|| !JS_DefineProperty(cx, obj, "from", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
 		return false;
 
+	if(((f->from_ip != NULL && *f->from_ip != '\0') || detail > file_detail_extdesc)
+		&& ((js_str = JS_NewStringCopyZ(cx, f->from_ip)) == NULL
+			|| !JS_DefineProperty(cx, obj, "from_ip_addr", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
+		return false;
+
+	if(((f->from_host != NULL && *f->from_host != '\0') || detail > file_detail_extdesc)
+		&& ((js_str = JS_NewStringCopyZ(cx, f->from_host)) == NULL
+			|| !JS_DefineProperty(cx, obj, "from_host_name", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
+		return false;
+
+	if(((f->from_prot != NULL && *f->from_prot != '\0') || detail > file_detail_extdesc)
+		&& ((js_str = JS_NewStringCopyZ(cx, f->from_prot)) == NULL
+			|| !JS_DefineProperty(cx, obj, "from_protocol", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
+		return false;
+
+	if(((f->from_port != NULL && *f->from_port != '\0') || detail > file_detail_extdesc)
+		&& ((js_str = JS_NewStringCopyZ(cx, f->from_port)) == NULL
+			|| !JS_DefineProperty(cx, obj, "from_port", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
+		return false;
+
 	if(((f->to_list != NULL && *f->to_list != '\0') || detail > file_detail_extdesc)
 		&& ((js_str = JS_NewStringCopyZ(cx, f->to_list)) == NULL
 			|| !JS_DefineProperty(cx, obj, "to_list", STRING_TO_JSVAL(js_str), NULL, NULL, flags)))
@@ -384,6 +404,66 @@ parse_file_properties(JSContext *cx, JSObject* obj, file_t* file, char** extdesc
 		}
 	}
 
+	prop_name = "from_ip_addr";
+	if(JS_GetProperty(cx, obj, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
+		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
+		HANDLE_PENDING(cx, cp);
+		if(cp == NULL) {
+			JS_ReportError(cx, "Invalid '%s' string in file object", prop_name);
+			return SMB_FAILURE;
+		}
+		if(smb_hfield_str(file, SENDERIPADDR, cp) != SMB_SUCCESS) {
+			free(cp);
+			JS_ReportError(cx, "Error %d adding '%s' property to file object", result, prop_name);
+			return result;
+		}
+	}
+
+	prop_name = "from_host_name";
+	if(JS_GetProperty(cx, obj, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
+		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
+		HANDLE_PENDING(cx, cp);
+		if(cp == NULL) {
+			JS_ReportError(cx, "Invalid '%s' string in file object", prop_name);
+			return SMB_FAILURE;
+		}
+		if(smb_hfield_str(file, SENDERHOSTNAME, cp) != SMB_SUCCESS) {
+			free(cp);
+			JS_ReportError(cx, "Error %d adding '%s' property to file object", result, prop_name);
+			return result;
+		}
+	}
+
+	prop_name = "from_protocol";
+	if(JS_GetProperty(cx, obj, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
+		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
+		HANDLE_PENDING(cx, cp);
+		if(cp == NULL) {
+			JS_ReportError(cx, "Invalid '%s' string in file object", prop_name);
+			return SMB_FAILURE;
+		}
+		if(smb_hfield_str(file, SENDERPROTOCOL, cp) != SMB_SUCCESS) {
+			free(cp);
+			JS_ReportError(cx, "Error %d adding '%s' property to file object", result, prop_name);
+			return result;
+		}
+	}
+
+	prop_name = "from_port";
+	if(JS_GetProperty(cx, obj, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
+		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
+		HANDLE_PENDING(cx, cp);
+		if(cp == NULL) {
+			JS_ReportError(cx, "Invalid '%s' string in file object", prop_name);
+			return SMB_FAILURE;
+		}
+		if(smb_hfield_str(file, SENDERPORT, cp) != SMB_SUCCESS) {
+			free(cp);
+			JS_ReportError(cx, "Error %d adding '%s' property to file object", result, prop_name);
+			return result;
+		}
+	}
+
 	prop_name = "desc";
 	if(JS_GetProperty(cx, obj, prop_name, &val) && !JSVAL_NULL_OR_VOID(val)) {
 		JSVALUE_TO_RASTRING(cx, val, cp, &cp_sz, NULL);
@@ -989,7 +1069,8 @@ js_add_file(JSContext *cx, uintN argc, jsval *arglist)
 	jsval*		argv = JS_ARGV(cx, arglist);
 	private_t*	p;
 	char*		extdesc = NULL;
-	file_t	file;
+	file_t		file;
+	client_t*	client = NULL;
 	bool		use_diz_always = false;
 	jsrefcount	rc;
 
@@ -1021,6 +1102,13 @@ js_add_file(JSContext *cx, uintN argc, jsval *arglist)
 		use_diz_always = JSVAL_TO_BOOLEAN(argv[argn]);
 		argn++;
 	}
+	if(argn < argc && JSVAL_IS_OBJECT(argv[argn]) && !JSVAL_IS_NULL(argv[argn])) {
+		JSObject* objarg = JSVAL_TO_OBJECT(argv[argn]);
+		JSClass* cl;
+		if((cl = JS_GetClass(cx, objarg)) != NULL && strcmp(cl->name, "Client") == 0) {
+			client = JS_GetPrivate(cx, objarg);
+		}
+	}
 
 	file.dir = p->smb.dirnum;
 	rc=JS_SUSPENDREQUEST(cx);
@@ -1032,6 +1120,7 @@ js_add_file(JSContext *cx, uintN argc, jsval *arglist)
 		}
 		char fpath[MAX_PATH + 1];
 		getfilepath(scfg, &file, fpath);
+		file_client_hfields(&file, client);
 		p->smb_result = smb_addfile(&p->smb, &file, SMB_SELFPACK, extdesc, fpath);
 		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(p->smb_result == SMB_SUCCESS));
 	}
@@ -1471,7 +1560,7 @@ static jsSyncMethodSpec js_filebase_functions[] = {
 		,31900
 	},
 	{"add",				js_add_file,		1, JSTYPE_BOOLEAN
-		,JSDOCSTR("file-meta-object [,use_diz_always=false]")
+		,JSDOCSTR("file-meta-object [,use_diz_always=false] [,object client=none]")
 		,JSDOCSTR("add a file to the file base")
 		,31900
 	},
diff --git a/src/sbbs3/listfile.cpp b/src/sbbs3/listfile.cpp
index aaebd1219229e5cf1098635ce9e4ba4fc448034e..8c2531944c14328e334e981d68d69f2adc129d92 100644
--- a/src/sbbs3/listfile.cpp
+++ b/src/sbbs3/listfile.cpp
@@ -954,7 +954,7 @@ int sbbs_t::listfileinfo(uint dirnum, const char *filespec, long mode)
 							sprintf(str,text[AddToOfflineDirQ]
 								,f->name,cfg.lib[cfg.dir[i]->lib]->sname,cfg.dir[i]->sname);
 							if(yesno(str)) {
-								addfile(&cfg, i, f, f->extdesc);
+								addfile(&cfg, i, f, f->extdesc, /* client: */NULL);
 							} 
 						} 
 					}
diff --git a/src/sbbs3/upload.cpp b/src/sbbs3/upload.cpp
index 2743d285b4396c114e08250a4f84628710e0f57d..0312da38efe0665b6910bbccd56b3e1f48730938 100644
--- a/src/sbbs3/upload.cpp
+++ b/src/sbbs3/upload.cpp
@@ -186,7 +186,7 @@ bool sbbs_t::uploadfile(file_t* f)
 	smb_hfield_bin(f, SMB_COST, cdt);
 	smb_hfield_str(f, SENDER, useron.alias);
 	bprintf(text[FileNBytesReceived],f->name,ultoac(length,tmp));
-	if(!addfile(&cfg, f->dir, f, ext))
+	if(!addfile(&cfg, f->dir, f, ext, &client))
 		return false;
 
 	safe_snprintf(str,sizeof(str),"uploaded %s to %s %s"